diff --git a/CONTEXT.md b/CONTEXT.md index b86432b198..9e8c720159 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -64,6 +64,126 @@ _Avoid_: independent per-robot timing, per-arm retiming when referring to coordi An execution-preparation artifact that derives control-task-specific joint trajectory messages from a generated trajectory without changing the generated trajectory's canonical global timing. _Avoid_: generated trajectory projection, execution-time parametrization, per-task generated trajectory +**Policy trajectory dispatch**: +An execution-preparation artifact that derives control-task-specific joint trajectory messages from policy action chunks while leaving control authority with the configured control task. +_Avoid_: direct policy control, policy motor write, learned controller bypass + +**Joint-trajectory policy chunk**: +A v1 robot-learning action chunk shaped to become the `JointTrajectory` argument accepted by a joint trajectory control task. +_Avoid_: generic policy action, direct motor command, backend-native tensor + +**Contract-assigned trajectory timing**: +A v1 policy rollout convention where the robot policy contract assigns simple relative timing to backend action rows when constructing a joint trajectory for dispatch, starting the first action row at `time_from_start = 0`. +_Avoid_: controller-side chunk timing inference, untimed policy positions, trajectory parametrization + +**Dispatch-owned trajectory timestamp**: +A policy rollout convention where the rollout module assigns the absolute intended start time for the whole contract-built trajectory chunk when submitting it to the control system. +_Avoid_: contract-owned dispatch time, backend inference timestamp as control timestamp, trajectory creation time + +**Subchunk trajectory dispatch**: +A policy rollout convention where the rollout module generates a larger policy action chunk but dispatches only a configured leading subset of action rows to the joint trajectory control task before replanning. +_Avoid_: executing the entire policy horizon by default, deep policy command queue, hidden action-horizon truncation + +**Completion-gated policy rollout**: +A simple rollout cadence where the policy module waits for the dispatched trajectory subset to finish before fetching the latest observations and generating the next backend action chunk. +_Avoid_: asynchronous continuous inference, speculative trajectory queueing, wall-clock-only rollout loop + +**Sleep-gated policy rollout**: +A v1 completion-gated policy rollout implementation where the policy module waits for the expected row coverage duration of the dispatched trajectory subset using the rollout clock before fetching observations and replanning. +_Avoid_: task-completion dependency, signal-based rollout synchronization, speculative inference + +**Policy action representation**: +The robot-learning action form a policy is trained or configured to produce before it is adapted for a DimOS control task. +_Avoid_: assuming all policy actions are joint positions, opaque policy output + +**Robot policy action**: +A runtime-independent robot-learning action emitted by a robot policy module after backend inference and contract conversion, before adaptation to benchmark runtime frames or real robot control commands. +_Avoid_: runtime action frame, motor command, backend tensor + +**Normalized robot policy action**: +A robot policy action whose numeric values remain in the policy-native normalized action space; the robot policy contract validates the normalized action while the control task owns physical execution scaling and safety mapping. +_Avoid_: denormalized controller command, physical joint target, unchecked backend output + +**Robot policy action chunk**: +A short-horizon sequence of robot policy actions produced by a policy backend for one observation, where DimOS owns how much of the chunk to execute before replanning. +_Avoid_: single waypoint when the backend predicts a horizon, blindly executed full horizon, backend-owned execution loop + +**Index-bounded policy chunk execution**: +A v1 policy chunk execution convention where a control task consumes a configured number of leading actions from each robot policy action chunk before accepting or waiting for the next chunk. +_Avoid_: time-window chunk execution, full-horizon execution, backend-owned chunk timing + +**Stale policy chunk deactivation**: +A policy chunk control behavior where the task stops contributing commands after its active chunk exceeds a configured staleness limit, allowing normal ControlCoordinator arbitration or hardware hold behavior to take over. +_Avoid_: continuing stale policy actions, forced zero command, hidden full-horizon fallback + +**Policy chunk inference trigger**: +A fast control-path signal from a policy chunk control task to a robot policy module requesting that a new robot policy action chunk be inferred asynchronously from the latest available observation. +_Avoid_: blocking policy inference call, direct chunk return from the control tick, external refill module + +**Policy chunk stream return**: +A live policy-control convention where robot policy action chunks requested by a fast inference trigger are delivered back to the ControlCoordinator through a stream rather than as the trigger RPC return value. +_Avoid_: polling for chunks, blocking trigger response, synchronous chunk handoff + +**Live policy parity gate**: +A real-policy validation run that checks the live policy stream path against the established benchmark policy path, expecting comparable policy success rather than merely proving plumbing with fake actions. +_Avoid_: fake-backend smoke as acceptance, plumbing-only realtime demo, single-episode proof + +**Live policy stream path**: +A stream-native rollout topology where ready robot policy observations flow into a robot policy module and runtime-independent robot policy actions flow out, primarily for physical robot rollout and secondarily for realtime simulator smoke tests of the same topology. +_Avoid_: fast benchmark path, synchronous eval path, realtime benchmark gate + +**Native benchmark action surface**: +A benchmark runtime action interface whose command values are defined by the benchmark environment itself rather than by a DimOS motor or joint surface. +_Avoid_: motor command alias, hidden joint target, controller-specific shortcut + +**LIBERO action mode**: +The simulator-side interpretation of `env.step(action)` in LIBERO, defining whether the action vector represents joint positions, relative end-effector deltas, or another environment-supported action form. +_Avoid_: DimOS controller, ControlCoordinator task, policy output type + +**Native LIBERO action mode**: +A LIBERO runtime mode, matching the official LeRobot LIBERO environment setup, that accepts the environment's own relative end-effector delta plus gripper action vector instead of a DimOS whole-body motor-position command. +_Avoid_: joint-position LIBERO profile, fake motor command, end-effector action as motor q + +**Runtime action frame**: +A runtime protocol command envelope for a named non-motor action surface, carrying action values and semantic identity without pretending they are joint positions or motor commands. +_Avoid_: overloaded motor frame, unnamed action vector, backend tensor leak + +**Action-surface control task**: +A control-coordinator task that accepts and validates commands for a semantic action surface while leaving the concrete runtime or robot mapping to the task implementation. +_Avoid_: joint trajectory task, motor adapter, policy bypass + +**Policy action control path**: +A live rollout control path where a robot policy action enters the ControlCoordinator as a first-class input and is handled by a policy-action-aware control task before becoming hardware commands. +_Avoid_: external policy command converter, direct runtime step path, policy module controller bypass + +**Robot policy contract**: +A robot-learning boundary that declares a specific robot/runtime and policy-backend input-output convention, including how aligned robot-native samples become backend-ready batches and how backend outputs become robot-native policy action chunks. +_Avoid_: execution contract, control-task adapter, universal robot contract + +**Robot policy observation**: +A runtime-independent policy input artifact that carries semantically named observation roles, timestamps, and policy-relevant metadata for robot-learning inference. +_Avoid_: benchmark sidecar response, runtime observation frame, backend-ready batch, robot learning sample + +**Backend-ready batch**: +The policy-backend-specific inference or training input produced from an aligned robot-native sample by a robot policy contract. +_Avoid_: universal DimOS batch, raw observation bundle, synchronized sample + +**Backend output**: +The policy-backend-specific action result that a robot policy contract converts into a robot-native policy action chunk. +_Avoid_: control command, joint trajectory, final actuator target + +**Backend output envelope**: +A small rollout artifact that carries a backend-native action result together with inference metadata needed for validation, tracing, and contract conversion. +_Avoid_: policy action chunk, control command, backend internals leak + +**Temporal sample readiness**: +The rollout-time check that the observation roles needed for a policy sample are available with acceptable freshness for the current inference tick, without requiring perfect cross-sensor timestamp equality. +_Avoid_: exact timestamp match, strict synchronization gate, semantic policy validation + +**Contract conversion failure**: +A fail-fast result when a robot policy contract receives a supposedly ready sample or backend output that violates the contract's declared semantic input-output convention. +_Avoid_: not-ready sample, silent coercion, best-effort backend batch + **Robokin kinematics backend**: A DimOS kinematics backend that presents multiple robokin-supported inverse-kinematics engines through one robotics-facing capability. _Avoid_: Oink backend, RoboKin world backend, single-engine Oink solver diff --git a/dimos/benchmark/runtime/test_libero_pro_sidecar_profile.py b/dimos/benchmark/runtime/test_libero_pro_sidecar_profile.py index 7913902be7..f8edffbc4e 100644 --- a/dimos/benchmark/runtime/test_libero_pro_sidecar_profile.py +++ b/dimos/benchmark/runtime/test_libero_pro_sidecar_profile.py @@ -20,6 +20,7 @@ from io import BytesIO from pathlib import Path import sys +from typing import Literal, cast import numpy as np import pytest @@ -31,11 +32,20 @@ sys.path.insert(0, str(LIBERO_PRO_SIDECAR_SRC)) from dimos_libero_pro_sidecar.server import ( + NATIVE_ABSOLUTE_ACTION_SPACE_ID, + NATIVE_ACTION_SPACE_ID, LiberoProRuntimeConfig, LiberoProRuntimeState, + RealLiberoBackend, + ensure_libero_config, validate_assets, ) -from dimos_runtime_protocol import EpisodeResetRequest, MotorActionFrame, StepRequest +from dimos_runtime_protocol import ( + EpisodeResetRequest, + MotorActionFrame, + RuntimeActionFrame, + StepRequest, +) class _FakeLiberoBackend: @@ -70,6 +80,64 @@ class _BadActionBackend(_FakeLiberoBackend): action_high = [1.0] * 7 +class _FakeNativeBackend(_FakeLiberoBackend): + action_low = [-1.0] * 7 + action_high = [1.0] * 7 + + def __init__(self) -> None: + self.last_action: list[float] = [] + + def step( + self, action: Sequence[float] + ) -> tuple[dict[str, object], float, bool, dict[str, object]]: + self.last_action = [float(item) for item in action] + return _fake_obs(self.last_action[:7], [0.0]), 0.5, False, {"success": False} + + +class _BadNativeBoundsBackend(_FakeNativeBackend): + action_low = [-0.5] * 7 + action_high = [1.0] * 7 + + +class _FakeAbsoluteNativeBackend(_FakeNativeBackend): + action_low = [-10.0] * 7 + action_high = [10.0] * 7 + + +class _FakeController: + def __init__(self) -> None: + self.use_delta = False + self.ee_ori_mat = np.eye(3, dtype=np.float32) + + +class _FakeRobot: + def __init__(self) -> None: + self.controller = _FakeController() + + +class _FakeNativeEnv: + action_spec = ([-1.0] * 7, [1.0] * 7) + + def __init__(self, **kwargs: object) -> None: + self.kwargs = kwargs + self.robots = [_FakeRobot()] + self.actions: list[list[float]] = [] + + def reset(self) -> dict[str, object]: + self.robots[0].controller.use_delta = False + return {"reset": True} + + def set_init_state(self, state: object) -> dict[str, object]: + return {"state": state} + + def step( + self, action: Sequence[float] + ) -> tuple[dict[str, object], float, bool, dict[str, object]]: + values = [float(item) for item in action] + self.actions.append(values) + return {"noop_count": len(self.actions)}, 0.0, False, {} + + def test_libero_pro_profile_maps_actions_states_score_and_payloads(tmp_path: Path) -> None: state = LiberoProRuntimeState(_config(tmp_path), backend=_FakeLiberoBackend()) @@ -89,6 +157,11 @@ def test_libero_pro_profile_maps_actions_states_score_and_payloads(tmp_path: Pat reset = state.reset(EpisodeResetRequest(episode_id="episode", task_id="task")) assert {frame.stream for frame in reset.observations} == {"robot_state", "agentview"} + state_frame = next(frame for frame in reset.observations if frame.stream == "robot_state") + assert state_frame.metadata["xvla_robot_state"] == { + "eef.pos": [[0.0, 0.0, 0.0]], + "eef.mat": [[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]], + } response = state.step( StepRequest( @@ -119,6 +192,215 @@ def test_libero_pro_rejects_incompatible_action_dimension(tmp_path: Path) -> Non LiberoProRuntimeState(_config(tmp_path), backend=_BadActionBackend()) +def test_libero_pro_native_mode_description_advertises_runtime_action_surface( + tmp_path: Path, +) -> None: + state = LiberoProRuntimeState( + _config(tmp_path, action_mode="native"), backend=_FakeNativeBackend() + ) + + description = state.describe() + + assert "runtime-action" in description.capabilities + assert description.metadata["action_mode"] == "native" + assert description.metadata["native_action_space_id"] == NATIVE_ACTION_SPACE_ID + assert description.metadata["action_shape"] == [7] + assert description.metadata["action_low"] == [-1.0] * 7 + assert description.metadata["action_high"] == [1.0] * 7 + assert description.metadata["controller"] == "JOINT_POSITION" + assert description.metadata["effective_controller"] == "OSC_POSE" + assert description.metadata["task_metadata"] == { + "benchmark_name": "libero_pro", + "task_order_index": 0, + "task_index": 0, + "task_name": "pick_up_the_black_bowl", + "init_state_index": 2, + } + assert description.metadata["language"] == "pick up the black bowl" + assert description.metadata["horizon"] == 1000 + assert description.metadata["effective_horizon"] == 1010 + assert description.metadata["reset_settle_steps"] == 10 + assert description.metadata["camera_config"] == { + "names": ["agentview"], + "height": 128, + "width": 128, + } + + +def test_libero_pro_native_mode_rejects_bad_action_spec(tmp_path: Path) -> None: + with pytest.raises(RuntimeError, match="bounds compatible"): + LiberoProRuntimeState( + _config(tmp_path, action_mode="native"), backend=_BadNativeBoundsBackend() + ) + + +def test_libero_pro_native_mode_steps_runtime_action_directly(tmp_path: Path) -> None: + backend = _FakeNativeBackend() + state = LiberoProRuntimeState(_config(tmp_path, action_mode="native"), backend=backend) + + response = state.step( + StepRequest( + episode_id="episode", + tick_id=1, + action=RuntimeActionFrame( + frame_type="runtime_action", + space_id=NATIVE_ACTION_SPACE_ID, + values=[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7], + tick_id=1, + ), + ) + ) + + assert backend.last_action == [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7] + assert response.reward == 0.5 + + +def test_libero_pro_absolute_native_mode_accepts_absolute_runtime_action( + tmp_path: Path, +) -> None: + backend = _FakeAbsoluteNativeBackend() + state = LiberoProRuntimeState(_config(tmp_path, action_mode="native_absolute"), backend=backend) + + description = state.describe() + response = state.step( + StepRequest( + episode_id="episode", + tick_id=1, + action=RuntimeActionFrame( + frame_type="runtime_action", + space_id=NATIVE_ABSOLUTE_ACTION_SPACE_ID, + values=[0.1, 0.2, 0.3, 1.4, -2.5, 0.6, 1.0], + tick_id=1, + ), + ) + ) + + assert description.metadata["action_mode"] == "native_absolute" + assert description.metadata["native_action_space_id"] == NATIVE_ABSOLUTE_ACTION_SPACE_ID + assert description.metadata["effective_controller"] == "OSC_POSE" + assert description.metadata["effective_horizon"] == 1000 + assert description.metadata["reset_settle_steps"] == 0 + assert backend.last_action == [0.1, 0.2, 0.3, 1.4, -2.5, 0.6, 1.0] + assert response.reward == 0.5 + + +def test_real_backend_native_reset_runs_lerobot_noops_and_use_delta( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + envs: list[_FakeNativeEnv] = [] + + class FakeBenchmark: + def get_task(self, task_index: int) -> object: + return type("Task", (), {"name": "task", "language": "language"})() + + def fake_env_cls(**kwargs: object) -> _FakeNativeEnv: + env = _FakeNativeEnv(**kwargs) + envs.append(env) + return env + + monkeypatch.setattr( + "dimos_libero_pro_sidecar.server.require_libero", + lambda *, visualize=False: ( + type( + "BenchmarkModule", + (), + {"get_benchmark": lambda self, name: lambda order: FakeBenchmark()}, + )(), + fake_env_cls, + ), + ) + monkeypatch.setattr("dimos_libero_pro_sidecar.server.ensure_libero_config", lambda *_: None) + monkeypatch.setattr("dimos_libero_pro_sidecar.server._load_init_states", lambda *_: ["init"]) + + backend = RealLiberoBackend(_config(tmp_path, action_mode="native")) + obs = backend.reset(0) + + env = envs[0] + assert env.kwargs["controller"] == "OSC_POSE" + assert env.kwargs["horizon"] == 1010 + assert env.robots[0].controller.use_delta is True + assert env.actions == [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0]] * 10 + assert obs == { + "noop_count": 10, + "robot0_eef_mat": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], + } + + +def test_real_backend_absolute_native_reset_preserves_absolute_controller( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + envs: list[_FakeNativeEnv] = [] + + class FakeBenchmark: + def get_task(self, task_index: int) -> object: + return type("Task", (), {"name": "task", "language": "language"})() + + def fake_env_cls(**kwargs: object) -> _FakeNativeEnv: + env = _FakeNativeEnv(**kwargs) + envs.append(env) + return env + + monkeypatch.setattr( + "dimos_libero_pro_sidecar.server.require_libero", + lambda *, visualize=False: ( + type( + "BenchmarkModule", + (), + {"get_benchmark": lambda self, name: lambda order: FakeBenchmark()}, + )(), + fake_env_cls, + ), + ) + monkeypatch.setattr("dimos_libero_pro_sidecar.server.ensure_libero_config", lambda *_: None) + monkeypatch.setattr("dimos_libero_pro_sidecar.server._load_init_states", lambda *_: ["init"]) + + backend = RealLiberoBackend(_config(tmp_path, action_mode="native_absolute")) + obs = backend.reset(0) + + env = envs[0] + assert env.kwargs["controller"] == "OSC_POSE" + assert env.kwargs["horizon"] == 1000 + assert env.robots[0].controller.use_delta is False + assert env.actions == [] + assert obs == { + "state": "init", + "robot0_eef_mat": [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], + } + + +def test_libero_pro_native_mode_rejects_motor_frame(tmp_path: Path) -> None: + state = LiberoProRuntimeState( + _config(tmp_path, action_mode="native"), backend=_FakeNativeBackend() + ) + + with pytest.raises(ValueError, match="native action mode requires RuntimeActionFrame"): + state.step( + StepRequest( + episode_id="episode", + tick_id=1, + action=MotorActionFrame(robot_id="panda", names=state.motor_names, q=[0.1] * 8), + ) + ) + + +def test_libero_pro_motor_mode_rejects_runtime_frame(tmp_path: Path) -> None: + state = LiberoProRuntimeState(_config(tmp_path), backend=_FakeLiberoBackend()) + + with pytest.raises(ValueError, match="motor action mode requires MotorActionFrame"): + state.step( + StepRequest( + episode_id="episode", + tick_id=1, + action=RuntimeActionFrame( + frame_type="runtime_action", + space_id=NATIVE_ACTION_SPACE_ID, + values=[0.1] * 7, + tick_id=1, + ), + ) + ) + + def test_libero_pro_rejects_unsupported_controller(tmp_path: Path) -> None: with pytest.raises(RuntimeError, match="unsupported LIBERO-PRO controller"): LiberoProRuntimeState( @@ -155,8 +437,27 @@ def test_libero_pro_asset_validation_does_not_bootstrap_by_default(tmp_path: Pat validate_assets(config) +def test_libero_config_is_created_noninteractively( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + config_root = tmp_path / "libero-config" + bddl_root = tmp_path / "libero" / "bddl_files" + init_states_root = tmp_path / "libero" / "init_files" + monkeypatch.setenv("LIBERO_CONFIG_PATH", str(config_root)) + + ensure_libero_config(bddl_root, init_states_root) + + config_text = (config_root / "config.yaml").read_text() + assert f"bddl_files: {bddl_root}" in config_text + assert f"init_states: {init_states_root}" in config_text + + def _config( - tmp_path: Path, *, controller: str = "JOINT_POSITION", visualize: bool = False + tmp_path: Path, + *, + action_mode: str = "motor", + controller: str = "JOINT_POSITION", + visualize: bool = False, ) -> LiberoProRuntimeConfig: bddl_root = tmp_path / "bddl" init_states_root = tmp_path / "init_states" @@ -170,6 +471,7 @@ def _config( benchmark_name="libero_pro", bddl_root=bddl_root, init_states_root=init_states_root, + action_mode=cast("Literal['motor', 'native', 'native_absolute']", action_mode), controller=controller, camera_names=("agentview",), init_state_index=2, @@ -183,6 +485,8 @@ def _fake_obs(joint_q: list[float], gripper_q: list[float]) -> dict[str, object] "robot0_joint_vel": [0.0] * len(joint_q), "robot0_gripper_qpos": gripper_q, "robot0_gripper_qvel": [0.0] * len(gripper_q), + "robot0_eef_pos": [0.0, 0.0, 0.0], + "robot0_eef_quat": [0.0, 0.0, 0.0, 1.0], "agentview_image": _pure_color_image(), } diff --git a/dimos/benchmark/runtime/test_runtime_sidecar_modules.py b/dimos/benchmark/runtime/test_runtime_sidecar_modules.py index e0665b057a..b269d2a9d2 100644 --- a/dimos/benchmark/runtime/test_runtime_sidecar_modules.py +++ b/dimos/benchmark/runtime/test_runtime_sidecar_modules.py @@ -224,6 +224,7 @@ def test_libero_pro_runtime_module_rpc_and_stream_outputs(mocker: _Mocker, tmp_p reset = module.reset(EpisodeResetRequest(episode_id="episode", task_id="task")) step = module.step(_step_request()) score = module.score() + snapshot_observations, snapshot_values = module.observation_snapshot() assert description.observation_streams == ["color_image", "camera_info", "runtime_event"] assert description.metadata["backend_camera_streams"] == ["agentview"] @@ -231,6 +232,16 @@ def test_libero_pro_runtime_module_rpc_and_stream_outputs(mocker: _Mocker, tmp_p assert "sync-http" not in reset.runtime_description.capabilities assert step.observations == [] assert score.score == 1.0 + assert {observation.stream for observation in snapshot_observations} >= { + "agentview", + "runtime_event", + } + image_snapshot = next( + observation for observation in snapshot_observations if observation.stream == "agentview" + ) + assert image_snapshot.data_ref == "/payloads/agentview-000001-000001.npy" + assert image_snapshot.metadata == {"camera_name": "agentview", "fov_y_deg": 45.0} + np.testing.assert_array_equal(snapshot_values["agentview"], _image()) _assert_published_native_types(module, expected_image=_image()) assert module._state is not None assert _StubLiberoState.init_count == 1 diff --git a/dimos/control/coordinator.py b/dimos/control/coordinator.py index 54022f5892..d54e3013fe 100644 --- a/dimos/control/coordinator.py +++ b/dimos/control/coordinator.py @@ -57,6 +57,11 @@ from dimos.msgs.geometry_msgs.PoseStamped import PoseStamped from dimos.msgs.geometry_msgs.Twist import Twist from dimos.msgs.sensor_msgs.JointState import JointState +from dimos.robot_learning.policy_rollout.models import RobotPolicyActionChunk +from dimos.robot_learning.policy_rollout.robot_policy_module import ( + PolicyChunkInferenceStatus, + RobotPolicyChunkInferenceSpec, +) from dimos.teleop.quest.quest_types import ( Buttons, ) @@ -140,6 +145,7 @@ class ControlCoordinator(Module): """ config: ControlCoordinatorConfig + _robot_policy: RobotPolicyChunkInferenceSpec | None # Output: Aggregated joint state for external consumers coordinator_joint_state: Out[JointState] @@ -157,10 +163,14 @@ class ControlCoordinator(Module): # Input: Teleop buttons for engage/disengage signaling teleop_buttons: In[Buttons] + # Input: Live robot-learning policy action chunks + robot_policy_action_chunk: In[RobotPolicyActionChunk] + # Arming and dry-run are one-shot RPCs, not streams. def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self._robot_policy = None # Connected hardware (keyed by hardware_id) self._hardware: dict[HardwareId, ConnectedHardware | ConnectedWholeBody] = {} @@ -181,6 +191,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self._cartesian_command_unsub: Callable[[], None] | None = None self._twist_command_unsub: Callable[[], None] | None = None self._buttons_unsub: Callable[[], None] | None = None + self._policy_chunk_unsub: Callable[[], None] | None = None logger.info(f"ControlCoordinator initialized at {self.config.tick_rate}Hz") @@ -278,7 +289,28 @@ def _create_task_from_config(self, cfg: TaskConfig) -> ControlTask: """Create a control task from config via the task registry.""" from dimos.control.tasks.registry import control_task_registry - return control_task_registry.create(cfg.type, cfg, hardware=self._hardware) + task = control_task_registry.create(cfg.type, cfg, hardware=self._hardware) + trigger_method = cfg.params.get("policy_trigger_method") or cfg.params.get( + "policy_trigger_config_name" + ) + if isinstance(trigger_method, str): + setter = getattr(task, "set_policy_trigger", None) + trigger = getattr(self, trigger_method, None) + if callable(setter) and callable(trigger): + setter(trigger) + return task + + @rpc + def trigger_policy_action_chunk_inference(self) -> PolicyChunkInferenceStatus: + """Request a live policy action chunk through the injected policy module ref.""" + + if self._robot_policy is None: + return PolicyChunkInferenceStatus( + accepted=False, + status="not_ready", + message="no robot policy module ref is connected", + ) + return self._robot_policy.trigger_action_chunk_inference() @rpc def add_hardware( @@ -526,6 +558,15 @@ def _on_buttons(self, msg: Buttons) -> None: for task in self._tasks.values(): task.on_buttons(msg) + def _on_robot_policy_action_chunk(self, chunk: RobotPolicyActionChunk) -> None: + """Forward robot policy action chunks to tasks that opt in.""" + t_now = time.perf_counter() + with self._task_lock: + for task in self._tasks.values(): + handler = getattr(task, "on_robot_policy_action_chunk", None) + if callable(handler): + handler(chunk, t_now) + @rpc def set_activated(self, engaged: bool) -> None: """Arm/disarm every task exposing ``arm()`` / ``disarm()``.""" @@ -687,6 +728,13 @@ def start(self) -> None: self._buttons_unsub = self.teleop_buttons.subscribe(self._on_buttons) logger.info("Subscribed to buttons for engage/disengage") + has_policy_chunk = any(t.type == "policy_chunk" for t in self.config.tasks) + if has_policy_chunk: + self._policy_chunk_unsub = self.robot_policy_action_chunk.subscribe( + self._on_robot_policy_action_chunk + ) + logger.info("Subscribed to robot_policy_action_chunk for policy tasks") + # Arming + dry-run are RPC-only; no stream subscription here. logger.info(f"ControlCoordinator started at {self.config.tick_rate}Hz") @@ -709,6 +757,9 @@ def stop(self) -> None: if self._buttons_unsub: self._buttons_unsub() self._buttons_unsub = None + if self._policy_chunk_unsub: + self._policy_chunk_unsub() + self._policy_chunk_unsub = None if self._tick_loop: self._tick_loop.stop() diff --git a/dimos/control/task.py b/dimos/control/task.py index 6e22e48246..8beba55749 100644 --- a/dimos/control/task.py +++ b/dimos/control/task.py @@ -326,3 +326,7 @@ def set_target_by_name(self, positions: dict[str, float], t_now: float) -> bool: def set_velocities_by_name(self, velocities: dict[str, float], t_now: float) -> bool: """No-op default.""" return False + + def on_robot_policy_action_chunk(self, chunk: object, t_now: float) -> bool: + """No-op default.""" + return False diff --git a/dimos/control/tasks/policy_chunk_task/__registry__.py b/dimos/control/tasks/policy_chunk_task/__registry__.py new file mode 100644 index 0000000000..b9f09c433c --- /dev/null +++ b/dimos/control/tasks/policy_chunk_task/__registry__.py @@ -0,0 +1,17 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +TASK_FACTORIES = { + "policy_chunk": "dimos.control.tasks.policy_chunk_task.policy_chunk_task:create_task", +} diff --git a/dimos/control/tasks/policy_chunk_task/policy_chunk_task.py b/dimos/control/tasks/policy_chunk_task/policy_chunk_task.py new file mode 100644 index 0000000000..9488dba046 --- /dev/null +++ b/dimos/control/tasks/policy_chunk_task/policy_chunk_task.py @@ -0,0 +1,286 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Control task for live robot policy action chunks.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +import math +import threading +from typing import Any, Literal + +from dimos.control.task import ( + BaseControlTask, + ControlMode, + CoordinatorState, + JointCommandOutput, + ResourceClaim, +) +from dimos.protocol.service.spec import BaseConfig +from dimos.robot_learning.policy_rollout.models import RobotPolicyActionChunk +from dimos.utils.logging_config import setup_logger + +logger = setup_logger() + +ActionMapping = Literal["delta", "target"] + + +@dataclass +class PolicyChunkControlTaskConfig: + joint_names: list[str] + accepted_action_space_id: str + priority: int = 10 + ticks_per_action: int = 1 + execute_first_n: int | None = None + stale_timeout_ticks: int = 10 + action_scale: float = 1.0 + action_mapping: ActionMapping = "delta" + gripper_joint_name: str | None = None + gripper_action_index: int | None = None + gripper_min: float = 0.0 + gripper_max: float = 1.0 + + +class PolicyChunkControlTask(BaseControlTask): + """Execute normalized robot policy action chunks as joint commands.""" + + def __init__(self, name: str, config: PolicyChunkControlTaskConfig) -> None: + if not config.joint_names: + raise ValueError(f"PolicyChunkControlTask '{name}' requires at least one joint") + if config.ticks_per_action < 1: + raise ValueError("ticks_per_action must be >= 1") + if config.stale_timeout_ticks < 0: + raise ValueError("stale_timeout_ticks must be >= 0") + self._name = name + self._config = config + self._joint_names_list = list(config.joint_names) + self._claimed_joints = set(config.joint_names) + if config.gripper_joint_name is not None: + self._claimed_joints.add(config.gripper_joint_name) + self._lock = threading.Lock() + self._actions: list[tuple[float, ...]] = [] + self._ticks_since_chunk = 0 + self._enabled = False + self._active = False + self._trigger: Callable[[], object] | None = None + self._trigger_in_flight = False + self._accepted_chunk_count = 0 + self._trigger_count = 0 + self._trigger_status_counts: dict[str, int] = {} + self._consumed_action_count = 0 + self._stale_deactivation_count = 0 + + @property + def name(self) -> str: + return self._name + + def claim(self) -> ResourceClaim: + return ResourceClaim( + joints=frozenset(self._claimed_joints), + priority=self._config.priority, + mode=ControlMode.SERVO_POSITION, + ) + + def is_active(self) -> bool: + with self._lock: + return self._enabled or self._active + + def start(self) -> None: + with self._lock: + self._enabled = True + + def stop(self) -> None: + with self._lock: + self._enabled = False + self._active = False + + def on_robot_policy_action_chunk(self, chunk: object, t_now: float) -> bool: + del t_now + if not isinstance(chunk, RobotPolicyActionChunk): + return False + if chunk.space_id != self._config.accepted_action_space_id: + logger.warning( + f"PolicyChunkControlTask {self._name}: rejected action space {chunk.space_id!r}" + ) + return False + if not self._validate_values(chunk.values): + return False + action_count = chunk.horizon + if self._config.execute_first_n is not None: + action_count = min(action_count, self._config.execute_first_n) + if action_count <= 0: + return False + with self._lock: + self._actions = list(chunk.values[:action_count]) + self._ticks_since_chunk = 0 + self._active = True + self._trigger_in_flight = False + self._accepted_chunk_count += 1 + return True + + def compute(self, state: CoordinatorState) -> JointCommandOutput | None: + del state.t_now + with self._lock: + if not self._active: + self._maybe_trigger_locked() + return None + if self._ticks_since_chunk > self._config.stale_timeout_ticks: + self._active = False + self._stale_deactivation_count += 1 + return None + action_index = self._ticks_since_chunk // self._config.ticks_per_action + if action_index >= len(self._actions): + self._active = False + self._maybe_trigger_locked() + return None + row = self._actions[action_index] + self._ticks_since_chunk += 1 + self._consumed_action_count += 1 + + positions = self._positions_from_row(row, state) + if positions is None: + return None + return JointCommandOutput( + joint_names=self._output_joint_names(), + positions=positions, + mode=ControlMode.SERVO_POSITION, + ) + + def set_policy_trigger(self, trigger: Callable[[], object]) -> None: + self._trigger = trigger + + def diagnostics(self) -> dict[str, int | dict[str, int]]: + with self._lock: + return { + "accepted_chunks": self._accepted_chunk_count, + "refill_triggers": self._trigger_count, + "inference_status_counts": dict(self._trigger_status_counts), + "consumed_actions": self._consumed_action_count, + "stale_deactivations": self._stale_deactivation_count, + } + + def on_preempted(self, by_task: str, joints: frozenset[str]) -> None: + if joints & self._claimed_joints: + logger.warning( + f"PolicyChunkControlTask {self._name} preempted by {by_task} on {joints}" + ) + + def _maybe_trigger_locked(self) -> None: + if self._trigger is None or self._trigger_in_flight: + return + self._trigger_in_flight = True + try: + result = self._trigger() + status = str(getattr(result, "status", "unknown")) + self._trigger_status_counts[status] = self._trigger_status_counts.get(status, 0) + 1 + accepted = getattr(result, "accepted", True) + if accepted: + self._trigger_count += 1 + else: + self._trigger_in_flight = False + except Exception: + self._trigger_in_flight = False + logger.exception(f"PolicyChunkControlTask {self._name}: policy trigger failed") + + def _validate_values(self, values: tuple[tuple[float, ...], ...]) -> bool: + required_dim = len(self._joint_names_list) + if self._config.gripper_joint_name is not None: + required_dim = max( + required_dim, (self._config.gripper_action_index or required_dim) + 1 + ) + action_dim = len(values[0]) + if action_dim < required_dim: + logger.warning( + f"PolicyChunkControlTask {self._name}: action_dim {action_dim} < required {required_dim}" + ) + return False + for row in values: + if len(row) != action_dim: + return False + for value in row: + if not math.isfinite(value) or value < -1.0 or value > 1.0: + logger.warning( + f"PolicyChunkControlTask {self._name}: invalid action value {value}" + ) + return False + return True + + def _positions_from_row( + self, row: tuple[float, ...], state: CoordinatorState + ) -> list[float] | None: + positions: list[float] = [] + for index, joint_name in enumerate(self._joint_names_list): + value = row[index] * self._config.action_scale + if self._config.action_mapping == "delta": + current = state.joints.get_position(joint_name) + if current is None: + return None + positions.append(current + value) + else: + positions.append(value) + if self._config.gripper_joint_name is not None: + gripper_index = self._config.gripper_action_index + if gripper_index is None: + gripper_index = len(self._joint_names_list) + normalized = (row[gripper_index] + 1.0) / 2.0 + positions.append( + self._config.gripper_min + + normalized * (self._config.gripper_max - self._config.gripper_min) + ) + return positions + + def _output_joint_names(self) -> list[str]: + names = list(self._joint_names_list) + if self._config.gripper_joint_name is not None: + names.append(self._config.gripper_joint_name) + return names + + +class PolicyChunkControlTaskParams(BaseConfig): + accepted_action_space_id: str + ticks_per_action: int = 1 + execute_first_n: int | None = None + stale_timeout_ticks: int = 10 + policy_trigger_method: str | None = None + policy_trigger_config_name: str | None = None + action_scale: float = 1.0 + action_mapping: ActionMapping = "delta" + gripper_joint_name: str | None = None + gripper_action_index: int | None = None + gripper_min: float = 0.0 + gripper_max: float = 1.0 + + +def create_task(cfg: Any, hardware: Any) -> PolicyChunkControlTask: + del hardware + params = PolicyChunkControlTaskParams.model_validate(cfg.params) + return PolicyChunkControlTask( + cfg.name, + PolicyChunkControlTaskConfig( + joint_names=cfg.joint_names, + accepted_action_space_id=params.accepted_action_space_id, + priority=cfg.priority, + ticks_per_action=params.ticks_per_action, + execute_first_n=params.execute_first_n, + stale_timeout_ticks=params.stale_timeout_ticks, + action_scale=params.action_scale, + action_mapping=params.action_mapping, + gripper_joint_name=params.gripper_joint_name, + gripper_action_index=params.gripper_action_index, + gripper_min=params.gripper_min, + gripper_max=params.gripper_max, + ), + ) diff --git a/dimos/control/tasks/policy_chunk_task/test_policy_chunk_task.py b/dimos/control/tasks/policy_chunk_task/test_policy_chunk_task.py new file mode 100644 index 0000000000..951f09c21d --- /dev/null +++ b/dimos/control/tasks/policy_chunk_task/test_policy_chunk_task.py @@ -0,0 +1,146 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import threading + +from dimos.control.coordinator import ControlCoordinator +from dimos.control.task import CoordinatorState, JointStateSnapshot +from dimos.control.tasks.policy_chunk_task.policy_chunk_task import ( + PolicyChunkControlTask, + PolicyChunkControlTaskConfig, +) +from dimos.robot_learning.policy_rollout.models import RobotPolicyActionChunk + + +def _state(pos: dict[str, float] | None = None) -> CoordinatorState: + return CoordinatorState( + joints=JointStateSnapshot(joint_positions=pos or {"arm/j0": 1.0, "arm/j1": 2.0}), + t_now=1.0, + dt=0.01, + ) + + +def _chunk(values: tuple[tuple[float, ...], ...], space: str = "space") -> RobotPolicyActionChunk: + return RobotPolicyActionChunk(space_id=space, values=values) + + +def _task(**kwargs: object) -> PolicyChunkControlTask: + config_kwargs = { + "joint_names": ["arm/j0", "arm/j1"], + "accepted_action_space_id": "space", + "action_scale": 0.1, + **kwargs, + } + return PolicyChunkControlTask("policy", PolicyChunkControlTaskConfig(**config_kwargs)) + + +def test_accepts_and_executes_prefix_with_ticks_per_action() -> None: + task = _task(ticks_per_action=2, execute_first_n=2) + assert task.on_robot_policy_action_chunk(_chunk(((0.5, -0.5), (1.0, 0.0), (-1.0, 1.0))), 1.0) + + first = task.compute(_state()) + second = task.compute(_state()) + third = task.compute(_state()) + + assert first is not None + assert first.joint_names == ["arm/j0", "arm/j1"] + assert first.positions == [1.05, 1.95] + assert second is not None + assert second.positions == [1.05, 1.95] + assert third is not None + assert third.positions == [1.1, 2.0] + + +def test_validation_rejects_wrong_space_and_bad_values() -> None: + task = _task() + assert not task.on_robot_policy_action_chunk(_chunk(((0.0, 0.0),), space="other"), 1.0) + assert not task.on_robot_policy_action_chunk(_chunk(((2.0, 0.0),)), 1.0) + assert not task.on_robot_policy_action_chunk(_chunk(((float("nan"), 0.0),)), 1.0) + assert not task.is_active() + + +def test_validation_rejects_unsupported_action_dim() -> None: + task = _task(gripper_joint_name="arm/gripper", gripper_action_index=2) + assert not task.on_robot_policy_action_chunk(_chunk(((0.0, 0.0),)), 1.0) + + +def test_refill_trigger_called_once_when_queue_empty() -> None: + calls = 0 + + def trigger() -> None: + nonlocal calls + calls += 1 + + task = _task() + task.set_policy_trigger(trigger) + assert task.compute(_state()) is None + assert task.compute(_state()) is None + assert calls == 1 + + +def test_refill_trigger_records_status_counts() -> None: + class Status: + accepted = False + status = "not_ready" + + task = _task() + task.set_policy_trigger(Status) + + assert task.compute(_state()) is None + + diagnostics = task.diagnostics() + assert diagnostics["refill_triggers"] == 0 + assert diagnostics["inference_status_counts"] == {"not_ready": 1} + + +def test_stale_deactivation_returns_no_command() -> None: + task = _task(stale_timeout_ticks=0) + assert task.on_robot_policy_action_chunk(_chunk(((0.0, 0.0), (0.0, 0.0))), 1.0) + assert task.compute(_state()) is not None + assert task.compute(_state()) is None + assert not task.is_active() + + +def test_gripper_mapping_appends_configured_joint() -> None: + task = _task( + gripper_joint_name="arm/gripper", + gripper_action_index=2, + gripper_min=0.02, + gripper_max=0.08, + ) + assert task.on_robot_policy_action_chunk(_chunk(((0.0, 0.0, 1.0),)), 1.0) + output = task.compute(_state()) + assert output is not None + assert output.joint_names == ["arm/j0", "arm/j1", "arm/gripper"] + assert output.positions == [1.0, 2.0, 0.08] + + +def test_coordinator_routes_policy_chunks_to_opt_in_tasks() -> None: + coordinator = object.__new__(ControlCoordinator) + task = _task(action_mapping="target") + coordinator._task_lock = threading.Lock() + coordinator._tasks = {task.name: task} + + coordinator._on_robot_policy_action_chunk(_chunk(((0.3, -0.3),))) + + output = task.compute(_state()) + assert output is not None + assert output.positions == [0.03, -0.03] + + +def test_claim_priority_supports_arbitration() -> None: + task = _task(priority=42) + claim = task.claim() + assert claim.priority == 42 + assert claim.joints == frozenset({"arm/j0", "arm/j1"}) diff --git a/dimos/robot_learning/policy_rollout/backends/backend.py b/dimos/robot_learning/policy_rollout/backends/backend.py new file mode 100644 index 0000000000..5d62991251 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/backends/backend.py @@ -0,0 +1,35 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Protocol + +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + PolicyBackendDescription, +) + + +class PolicyBackend(Protocol): + """Batch-first policy backend interface used by RobotPolicyModule.""" + + def initialize(self) -> None: ... + + def reset_episode(self) -> None: ... + + def infer_batch(self, batch: BackendBatch) -> BackendOutputEnvelope: ... + + def close(self) -> None: ... + + def describe(self) -> PolicyBackendDescription: ... diff --git a/dimos/robot_learning/policy_rollout/backends/fixed/__init__.py b/dimos/robot_learning/policy_rollout/backends/fixed/__init__.py new file mode 100644 index 0000000000..f2dd7de9af --- /dev/null +++ b/dimos/robot_learning/policy_rollout/backends/fixed/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Fixed-action policy backend for rollout smoke tests.""" diff --git a/dimos/robot_learning/policy_rollout/backends/fixed/__registry__.py b/dimos/robot_learning/policy_rollout/backends/fixed/__registry__.py new file mode 100644 index 0000000000..10d9e6b235 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/backends/fixed/__registry__.py @@ -0,0 +1,19 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Registry manifest for the fixed-action policy backend.""" + +POLICY_BACKENDS = { + "fixed_action": "dimos.robot_learning.policy_rollout.backends.fixed.backend:FixedActionBackend", +} diff --git a/dimos/robot_learning/policy_rollout/backends/fixed/backend.py b/dimos/robot_learning/policy_rollout/backends/fixed/backend.py new file mode 100644 index 0000000000..98e9e8ffec --- /dev/null +++ b/dimos/robot_learning/policy_rollout/backends/fixed/backend.py @@ -0,0 +1,68 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Fixed-action backend for lightweight policy rollout tests.""" + +from collections.abc import Sequence + +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + PolicyBackendDescription, +) + + +class FixedActionBackend: + """Policy backend test double that emits a configured 7D action.""" + + def __init__(self, action: Sequence[float], *, use_action_chunk: bool = False) -> None: + if len(action) != 7: + raise ValueError("fixed action must have exactly 7 values") + self._action = tuple(float(value) for value in action) + self._use_action_chunk = use_action_chunk + self._initialized = False + self._episode_resets = 0 + + def initialize(self) -> None: + self._initialized = True + + def reset_episode(self) -> None: + self._episode_resets += 1 + + def infer_batch(self, batch: BackendBatch) -> BackendOutputEnvelope: + if not self._initialized: + raise RuntimeError("FixedActionBackend was not initialized") + output: tuple[float, ...] | tuple[tuple[float, ...], ...] = self._action + if self._use_action_chunk: + output = (self._action,) + return BackendOutputEnvelope( + output=output, + metadata={ + "backend_type": "fixed_action", + "batch_metadata": dict(batch.metadata), + "episode_resets": self._episode_resets, + "use_action_chunk": self._use_action_chunk, + }, + ) + + def close(self) -> None: + self._initialized = False + + def describe(self) -> PolicyBackendDescription: + return PolicyBackendDescription( + backend_type="fixed_action", + checkpoint_id=None, + supports_episode_reset=True, + metadata={"action": list(self._action), "episode_resets": self._episode_resets}, + ) diff --git a/dimos/robot_learning/policy_rollout/backends/lerobot/__registry__.py b/dimos/robot_learning/policy_rollout/backends/lerobot/__registry__.py new file mode 100644 index 0000000000..14023bc507 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/backends/lerobot/__registry__.py @@ -0,0 +1,17 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +POLICY_BACKENDS = { + "lerobot": "dimos.robot_learning.policy_rollout.backends.lerobot.backend:create_backend", +} diff --git a/dimos/robot_learning/policy_rollout/backends/lerobot/backend.py b/dimos/robot_learning/policy_rollout/backends/lerobot/backend.py new file mode 100644 index 0000000000..d7c5736b80 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/backends/lerobot/backend.py @@ -0,0 +1,377 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from collections.abc import Callable, Mapping +from contextlib import AbstractContextManager, nullcontext +from dataclasses import dataclass +from importlib import import_module +from typing import cast + +import numpy as np + +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + PolicyBackendDescription, +) + +_lerobot_make_pre_post_processors: object | None +_LEROBOT_PROCESSOR_IMPORT_ERROR: ImportError | None + +try: + from lerobot.policies import ( # type: ignore[import-not-found] + make_pre_post_processors as _lerobot_make_pre_post_processors, + ) + + _LEROBOT_PROCESSOR_IMPORT_ERROR = None +except ImportError as exc: + try: + from lerobot.policies.factory import ( # type: ignore[import-not-found] + make_pre_post_processors as _lerobot_make_pre_post_processors, + ) + + _LEROBOT_PROCESSOR_IMPORT_ERROR = None + except ImportError: + _lerobot_make_pre_post_processors = None + _LEROBOT_PROCESSOR_IMPORT_ERROR = exc + +_DEFAULT_POLICY_FAMILY = "vla_jepa" + + +@dataclass(frozen=True) +class _LeRobotPolicyFamilySpec: + family: str + default_checkpoint: str + module: str + class_name: str + install_extra: str + + @property + def class_path(self) -> str: + return f"{self.module}.{self.class_name}" + + +_POLICY_FAMILIES = { + "vla_jepa": _LeRobotPolicyFamilySpec( + family="vla_jepa", + default_checkpoint="lerobot/VLA-JEPA-LIBERO", + module="lerobot.policies.vla_jepa.modeling_vla_jepa", + class_name="VLAJEPAPolicy", + install_extra="vla_jepa", + ), + "xvla": _LeRobotPolicyFamilySpec( + family="xvla", + default_checkpoint="lerobot/xvla-libero", + module="lerobot.policies.xvla.modeling_xvla", + class_name="XVLAPolicy", + install_extra="xvla", + ), +} +_DEFAULT_CHECKPOINT = _POLICY_FAMILIES[_DEFAULT_POLICY_FAMILY].default_checkpoint + + +class LeRobotBackend: + """Optional in-process LeRobot policy backend for LIBERO policy families.""" + + def __init__( + self, + *, + checkpoint_id: str | None = None, + policy_family: str = _DEFAULT_POLICY_FAMILY, + device: str | None = None, + use_action_chunk: bool = False, + ) -> None: + self._family_spec = _policy_family_spec(policy_family) + self._checkpoint_id = checkpoint_id or self._family_spec.default_checkpoint + self._device = device + self._use_action_chunk = use_action_chunk + self._policy: object | None = None + self._preprocessor: Callable[[Mapping[str, object]], Mapping[str, object]] | None = None + self._postprocessor: Callable[[object], object] | None = None + self._policy_class_name: str | None = None + self._processor_source: str | None = None + self._supports_action_chunk_inference: bool | None = None + + def initialize(self) -> None: + if self._policy is not None: + return + policy_cls = _load_policy_class(self._family_spec) + from_pretrained = getattr(policy_cls, "from_pretrained", None) + if not callable(from_pretrained): + raise RuntimeError( + f"LeRobot {self._family_spec.class_name}.from_pretrained was not found" + ) + policy = from_pretrained(self._checkpoint_id) + self._policy_class_name = _qualified_name(policy) + self._supports_action_chunk_inference = callable( + getattr(policy, "predict_action_chunk", None) + ) + self._configure_device(policy) + eval_policy = getattr(policy, "eval", None) + if callable(eval_policy): + eval_policy() + self._policy = policy + self._prepare_processors(policy) + + def reset_episode(self) -> None: + policy = self._require_policy() + reset_policy = getattr(policy, "reset", None) + if callable(reset_policy): + reset_policy() + + def infer_batch(self, batch: BackendBatch) -> BackendOutputEnvelope: + policy = self._require_policy() + backend_batch: Mapping[str, object] = _tensorized_preprocessor_input(batch.payload) + if self._preprocessor is not None: + backend_batch = self._preprocessor(backend_batch) + with _torch_no_grad(): + output = self._infer(policy, backend_batch) + if self._postprocessor is not None: + output = self._postprocessor(output) + action = _action_output_tuple(output, preserve_rank=self._use_action_chunk) + return BackendOutputEnvelope( + output=action, + metadata={ + "backend_type": "lerobot", + "policy_family": self._family_spec.family, + "checkpoint_id": self._checkpoint_id, + "inference_method": "predict_action_chunk" + if self._use_action_chunk + else "select_action", + "output_shape": _shape_of(output), + "batch_metadata": dict(batch.metadata), + }, + ) + + def close(self) -> None: + self._policy = None + self._preprocessor = None + self._postprocessor = None + + def describe(self) -> PolicyBackendDescription: + return PolicyBackendDescription( + backend_type="lerobot", + checkpoint_id=self._checkpoint_id, + device=self._resolved_device(), + policy_class=self._policy_class_name, + supports_episode_reset=True, + metadata={ + "policy_family": self._family_spec.family, + "use_action_chunk": self._use_action_chunk, + "inference_method": "predict_action_chunk" + if self._use_action_chunk + else "select_action", + "configured_policy_class": self._family_spec.class_path, + "supports_action_chunk_inference": self._supports_action_chunk_inference, + "processor_source": self._processor_source, + }, + ) + + def _infer(self, policy: object, batch: Mapping[str, object]) -> object: + method_name = "predict_action_chunk" if self._use_action_chunk else "select_action" + infer = getattr(policy, method_name, None) + if not callable(infer): + raise RuntimeError( + f"LeRobot {self._family_spec.family} policy does not expose {method_name}" + ) + return infer(batch) + + def _configure_device(self, policy: object) -> None: + if self._device is None: + return + config = getattr(policy, "config", None) + if config is not None: + config.device = self._device # type: ignore[attr-defined] + to_device = getattr(policy, "to", None) + if callable(to_device): + to_device(self._device) + + def _prepare_processors(self, policy: object) -> None: + config = getattr(policy, "config", None) + if config is None: + raise RuntimeError("LeRobot policy does not expose config for processors") + device = self._resolved_device() + processors = _make_pre_post_processors( + self._family_spec, + policy_cfg=config, + pretrained_path=self._checkpoint_id, + preprocessor_overrides={"device_processor": {"device": str(device)}} + if device is not None + else None, + ) + self._install_processors(processors) + self._processor_source = "checkpoint" + + def _install_processors(self, processors: object) -> None: + preprocessor, postprocessor = cast("tuple[object, object]", processors) + if callable(preprocessor): + self._preprocessor = cast( + "Callable[[Mapping[str, object]], Mapping[str, object]]", preprocessor + ) + if callable(postprocessor): + self._postprocessor = postprocessor + + def _require_policy(self) -> object: + self.initialize() + if self._policy is None: + raise RuntimeError("LeRobot policy did not initialize") + return self._policy + + def _resolved_device(self) -> str | None: + if self._device is not None: + return self._device + if self._policy is None: + return None + config = getattr(self._policy, "config", None) + device = getattr(config, "device", None) + return str(device) if device is not None else None + + +def _policy_family_spec(policy_family: str) -> _LeRobotPolicyFamilySpec: + try: + return _POLICY_FAMILIES[policy_family] + except KeyError as exc: + families = ", ".join(sorted(_POLICY_FAMILIES)) + raise ValueError( + f"unsupported LeRobot policy family {policy_family!r}; expected one of {families}" + ) from exc + + +def _load_policy_class(spec: _LeRobotPolicyFamilySpec) -> type[object]: + try: + module = import_module(spec.module) + except ImportError as exc: + raise RuntimeError(_family_install_hint(spec)) from exc + policy_cls = getattr(module, spec.class_name, None) + if policy_cls is None: + raise RuntimeError(f"LeRobot {spec.class_path} was not found") + return cast("type[object]", policy_cls) + + +def _family_install_hint(spec: _LeRobotPolicyFamilySpec) -> str: + return ( + f"Install LeRobot from GitHub main with the {spec.install_extra} extra to use " + f"LeRobotBackend policy_family={spec.family!r} for {spec.default_checkpoint}. " + "Example: uv run --with " + f'"lerobot[{spec.install_extra}] @ git+https://github.com/huggingface/lerobot.git" ...' + ) + + +def _make_pre_post_processors( + spec: _LeRobotPolicyFamilySpec, + **kwargs: object, +) -> tuple[object, object]: + if not callable(_lerobot_make_pre_post_processors): + raise RuntimeError( + "LeRobot processor factory was not found; " + _family_install_hint(spec) + ) from _LEROBOT_PROCESSOR_IMPORT_ERROR + return cast("tuple[object, object]", _lerobot_make_pre_post_processors(**kwargs)) + + +def _torch_no_grad() -> AbstractContextManager[object]: + try: + import torch # type: ignore[import-not-found] + except ImportError: + return nullcontext() + return cast("AbstractContextManager[object]", torch.no_grad()) + + +def _qualified_name(value: object) -> str: + cls = value.__class__ + return f"{cls.__module__}.{cls.__qualname__}" + + +def _tensorized_preprocessor_input(payload: Mapping[str, object]) -> dict[str, object]: + prepared: dict[str, object] = {} + for key, value in payload.items(): + if isinstance(value, np.ndarray): + prepared[key] = _to_torch_tensor(key, value) + else: + prepared[key] = value + return prepared + + +def _to_torch_tensor(key: str, value: np.ndarray) -> object: + try: + import torch # type: ignore[import-not-found] + except ImportError as exc: + raise RuntimeError("Install torch to tensorize LeRobot backend inputs") from exc + array = value + if key.startswith("observation.images.") and array.ndim == 3: + if array.shape[-1] in (1, 3): + array = np.transpose(array, (2, 0, 1)) + if array.dtype == np.uint8: + array = array.astype(np.float32) / 255.0 + return torch.as_tensor(array, dtype=torch.float32) + + +def _flat_float_tuple(value: object) -> tuple[float, ...]: + array = _as_numpy_array(value).reshape(-1) + return tuple(float(item) for item in array) + + +def _action_output_tuple( + value: object, + *, + preserve_rank: bool, +) -> tuple[float, ...] | tuple[tuple[float, ...], ...]: + if not preserve_rank: + return _flat_float_tuple(value) + array = _as_numpy_array(value) + if array.ndim == 3 and array.shape[0] == 1: + array = array[0] + if array.ndim != 2: + raise ValueError(f"LeRobot action chunk output must have rank 2, got shape {array.shape}") + return tuple(tuple(float(item) for item in row) for row in array) + + +def _as_numpy_array(value: object) -> np.ndarray: + if isinstance(value, np.ndarray): + return value.astype(np.float32, copy=False) + detach = getattr(value, "detach", None) + if callable(detach): + value = detach() + cpu = getattr(value, "cpu", None) + if callable(cpu): + value = cpu() + numpy = getattr(value, "numpy", None) + if callable(numpy): + return cast("np.ndarray", numpy()).astype(np.float32, copy=False) + return np.asarray(value, dtype=np.float32) + + +def _shape_of(value: object) -> list[int]: + shape = getattr(value, "shape", None) + if shape is not None: + return [int(dim) for dim in shape] + if isinstance(value, (list, tuple)): + if value and isinstance(value[0], (list, tuple)): + return [len(value), len(value[0])] + return [len(value)] + return [] + + +def create_backend(**params: object) -> LeRobotBackend: + checkpoint_id = params.get("checkpoint_id") + policy_family = params.get("policy_family", _DEFAULT_POLICY_FAMILY) + device = params.get("device") + use_action_chunk = params.get("use_action_chunk", False) + return LeRobotBackend( + checkpoint_id=str(checkpoint_id) if checkpoint_id is not None else None, + policy_family=str(policy_family), + device=cast("str | None", device), + use_action_chunk=bool(use_action_chunk), + ) diff --git a/dimos/robot_learning/policy_rollout/contract.py b/dimos/robot_learning/policy_rollout/contract.py new file mode 100644 index 0000000000..ea13aa3d9f --- /dev/null +++ b/dimos/robot_learning/policy_rollout/contract.py @@ -0,0 +1,35 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Protocol + +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + RobotPolicyAction, + RobotPolicyActionChunk, + RobotPolicyObservation, +) + + +class RobotPolicyContract(Protocol): + """Semantic boundary between runtime samples, backend batches, and actions.""" + + def to_backend_batch(self, sample: RobotPolicyObservation) -> BackendBatch: ... + + def from_backend_output(self, output: BackendOutputEnvelope) -> RobotPolicyAction: ... + + def chunk_from_backend_output( + self, output: BackendOutputEnvelope + ) -> RobotPolicyActionChunk: ... diff --git a/dimos/robot_learning/policy_rollout/contracts/vla_jepa_libero/__registry__.py b/dimos/robot_learning/policy_rollout/contracts/vla_jepa_libero/__registry__.py new file mode 100644 index 0000000000..d196a2f0bc --- /dev/null +++ b/dimos/robot_learning/policy_rollout/contracts/vla_jepa_libero/__registry__.py @@ -0,0 +1,17 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ROBOT_POLICY_CONTRACTS = { + "vla_jepa_libero": "dimos.robot_learning.policy_rollout.vla_jepa_libero_contract:create_contract", +} diff --git a/dimos/robot_learning/policy_rollout/contracts/xvla_libero/__registry__.py b/dimos/robot_learning/policy_rollout/contracts/xvla_libero/__registry__.py new file mode 100644 index 0000000000..d112cf1495 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/contracts/xvla_libero/__registry__.py @@ -0,0 +1,17 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ROBOT_POLICY_CONTRACTS = { + "xvla_libero": "dimos.robot_learning.policy_rollout.xvla_libero_contract:create_contract", +} diff --git a/dimos/robot_learning/policy_rollout/evaluation.py b/dimos/robot_learning/policy_rollout/evaluation.py new file mode 100644 index 0000000000..2a7881853d --- /dev/null +++ b/dimos/robot_learning/policy_rollout/evaluation.py @@ -0,0 +1,530 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from dataclasses import asdict, dataclass, field, is_dataclass +import json +from pathlib import Path +from typing import Protocol + +from dimos_runtime_protocol import ( + EpisodeResetRequest, + EpisodeResetResponse, + ObservationFrame, + ObservationKind, + RuntimeActionFrame, + RuntimeDescription, + StepRequest, + StepResponse, +) +import imageio.v2 as imageio +import numpy as np + +from dimos.benchmark.runtime.artifacts import write_json +from dimos.core.coordination.blueprints import Blueprint, autoconnect +from dimos.core.core import rpc +from dimos.core.module import Module, ModuleConfig +from dimos.robot_learning.policy_rollout.models import ( + JsonObject, + RobotPolicyAction, + RobotPolicyObservation, +) +from dimos.robot_learning.policy_rollout.robot_policy_module import RobotPolicyModule +from dimos.spec.utils import Spec + + +@dataclass(frozen=True) +class BenchmarkEpisodeSpec: + """One benchmark episode selected by the evaluation runner.""" + + episode_id: str + task_id: str + task_index: int + init_state_index: int + seed: int | None = None + options: JsonObject = field(default_factory=dict) + + +@dataclass(frozen=True) +class BenchmarkEpisodeResult: + """Serializable per-episode policy evaluation artifact.""" + + episode_id: str + task_id: str + task_index: int + init_state_index: int + success: bool + steps: int + reward_sum: float + done: bool + failure_reason: str | None + action_shape: tuple[int, ...] + action_min: float | None + action_max: float | None + observed_streams: tuple[str, ...] + + +@dataclass(frozen=True) +class BenchmarkEvaluationSummary: + """Serializable aggregate policy evaluation artifact.""" + + episodes: int + successes: int + success_rate: float + success_threshold: float + passed: bool + + +@dataclass(frozen=True) +class RuntimeStreamSnapshot: + """Module-native stream values captured for one runtime reset or step tick.""" + + observations: tuple[ObservationFrame, ...] = () + values: Mapping[str, object] = field(default_factory=dict) + + @property + def observed_streams(self) -> tuple[str, ...]: + streams: list[str] = [] + for stream in self.values: + if stream not in streams: + streams.append(stream) + for frame in self.observations: + if frame.stream not in streams: + streams.append(frame.stream) + return tuple(streams) + + +class PolicyEvalRuntimeSession: + """Module-native runtime seam used by policy evaluation.""" + + def reset(self, request: EpisodeResetRequest) -> EpisodeResetResponse: + raise NotImplementedError + + def step(self, request: StepRequest) -> StepResponse: + raise NotImplementedError + + def latest_observation_snapshot(self) -> RuntimeStreamSnapshot: + raise NotImplementedError + + def close(self) -> None: + pass + + +class PolicyActionSource(Spec, Protocol): + def reset(self, episode_id: str | None = None) -> None: ... + + def infer_action(self, sample: RobotPolicyObservation) -> RobotPolicyAction: ... + + def close(self) -> None: ... + + def describe_backend(self) -> object: ... + + +class BenchmarkPolicyEvalModuleConfig(ModuleConfig): + """Config for module-compatible benchmark policy evaluation.""" + + artifact_dir: str = "artifacts/benchmark/policy-eval" + max_steps: int = 1000 + success_threshold: float = 0.50 + close_policy_on_finish: bool = False + video_dir: str | None = None + video_streams: tuple[str, ...] = () + video_fps: int = 20 + + +class BenchmarkPolicyEvalModule(Module): + """DimOS module wrapper for lockstep benchmark policy evaluation. + + V1 keeps runtime and policy calls RPC-style/lockstep. The demo script may + inject a local runtime client and policy module, while future blueprints can + supply remote module clients behind the same protocol surface. + """ + + config: BenchmarkPolicyEvalModuleConfig + robot_policy_module: PolicyActionSource | None + + def __init__( + self, + runtime_session: PolicyEvalRuntimeSession | None = None, + robot_policy_module: PolicyActionSource | None = None, + **kwargs: object, + ) -> None: + super().__init__(**kwargs) + self._runtime_session = runtime_session + self.robot_policy_module = robot_policy_module + self._last_results: tuple[BenchmarkEpisodeResult, ...] = () + + @property + def last_results(self) -> tuple[BenchmarkEpisodeResult, ...]: + return self._last_results + + def configure( + self, + *, + runtime_session: PolicyEvalRuntimeSession, + robot_policy_module: PolicyActionSource, + ) -> None: + self._runtime_session = runtime_session + self.robot_policy_module = robot_policy_module + + @rpc + def run_episodes(self, episodes: list[BenchmarkEpisodeSpec]) -> BenchmarkEvaluationSummary: + policy_module = self.robot_policy_module + if self._runtime_session is None or policy_module is None: + raise RuntimeError( + "BenchmarkPolicyEvalModule requires runtime session and policy client" + ) + runner = BenchmarkPolicyEvalRunner( + runtime_session=self._runtime_session, + robot_policy_module=policy_module, + artifact_dir=Path(self.config.artifact_dir), + max_steps=self.config.max_steps, + success_threshold=self.config.success_threshold, + close_policy_on_finish=self.config.close_policy_on_finish, + video_dir=Path(self.config.video_dir) if self.config.video_dir is not None else None, + video_streams=self.config.video_streams, + video_fps=self.config.video_fps, + ) + summary = runner.run(episodes) + self._last_results = runner.last_results + return summary + + def close(self) -> None: + self._close_module() + + @rpc + def stop(self) -> None: + if self.config.close_policy_on_finish and self.robot_policy_module is not None: + self.robot_policy_module.close() + super().stop() + + +def lerobot_libero_policy_eval_blueprint( + *, + checkpoint_id: str = "lerobot/VLA-JEPA-LIBERO", + device: str | None = None, + artifact_dir: str = "artifacts/benchmark/lerobot-vla-jepa-libero", + max_steps: int = 1000, + success_threshold: float = 0.50, +) -> Blueprint: + """Create the module-shaped LeRobot LIBERO policy-evaluation blueprint. + + The runtime sidecar is still created by the benchmark/demo orchestration per + episode; this blueprint captures the policy module and evaluation module + configuration for DimOS module composition and future runtime-client remaps. + """ + + backend_params: dict[str, object] = {"checkpoint_id": checkpoint_id} + if device is not None: + backend_params["device"] = device + return autoconnect( + RobotPolicyModule.blueprint( + backend_type="lerobot", + backend_params=backend_params, + contract_type="vla_jepa_libero", + contract_params={}, + ), + BenchmarkPolicyEvalModule.blueprint( + artifact_dir=artifact_dir, + max_steps=max_steps, + success_threshold=success_threshold, + ), + ) + + +class LiberoRobotPolicyObservationBuilder: + """Convert LIBERO runtime observations into RobotPolicyObservation instances.""" + + def build( + self, + *, + episode: BenchmarkEpisodeSpec, + tick_id: int, + observations: Sequence[ObservationFrame], + runtime_description: RuntimeDescription, + observation_values: Mapping[str, object], + reward: float = 0.0, + done: bool = False, + success: bool | None = None, + ) -> RobotPolicyObservation: + del episode, tick_id + values: dict[str, object] = dict(observation_values) + for frame in observations: + if frame.kind == "state" or frame.kind == ObservationKind.STATE: + state = frame.metadata.get("state", frame.metadata.get("values")) + if state is not None: + values[frame.stream] = state + xvla_robot_state = frame.metadata.get("xvla_robot_state") + if xvla_robot_state is not None: + values["xvla_robot_state"] = xvla_robot_state + language = runtime_description.metadata.get("language") + task = language if isinstance(language, str) else None + metadata: dict[str, object] = { + "reward": reward, + "done": done, + "success": success, + "observed_streams": tuple(frame.stream for frame in observations), + "runtime_metadata": dict(runtime_description.metadata), + } + if task is not None: + metadata["language"] = task + return RobotPolicyObservation( + observations=values, + metadata=metadata, + ) + + +class BenchmarkPolicyEvalRunner: + """Own benchmark lifecycle and execute robot-policy actions through runtime client. + + RobotPolicyModule owns inference only. This runner owns episode selection, + runtime reset/step timing, policy reset calls, metrics, success gates, and + artifact output. + """ + + def __init__( + self, + *, + runtime_session: PolicyEvalRuntimeSession, + robot_policy_module: PolicyActionSource, + artifact_dir: Path, + max_steps: int, + success_threshold: float = 0.50, + close_policy_on_finish: bool = True, + video_dir: Path | None = None, + video_streams: Sequence[str] = (), + video_fps: int = 20, + ) -> None: + if max_steps <= 0: + raise ValueError("max_steps must be positive") + if video_fps <= 0: + raise ValueError("video_fps must be positive") + self._runtime_session = runtime_session + self._robot_policy_module = robot_policy_module + self._artifact_dir = artifact_dir + self._max_steps = max_steps + self._success_threshold = success_threshold + self._close_policy_on_finish = close_policy_on_finish + self._video_dir = video_dir + self._video_streams = tuple(video_streams) + self._video_fps = video_fps + self._sample_builder = LiberoRobotPolicyObservationBuilder() + self._last_results: tuple[BenchmarkEpisodeResult, ...] = () + + @property + def last_results(self) -> tuple[BenchmarkEpisodeResult, ...]: + """Most recent episode results written by this runner.""" + + return self._last_results + + def run(self, episodes: Sequence[BenchmarkEpisodeSpec]) -> BenchmarkEvaluationSummary: + """Run all episodes and write required benchmark artifacts.""" + + results: list[BenchmarkEpisodeResult] = [] + runtime_description: RuntimeDescription | None = None + try: + for episode in episodes: + result, runtime_description = self._run_episode(episode) + results.append(result) + finally: + if self._close_policy_on_finish: + self._robot_policy_module.close() + + self._last_results = tuple(results) + summary = self._summary(results) + self._write_artifacts(results, summary, runtime_description) + return summary + + def _run_episode( + self, episode: BenchmarkEpisodeSpec + ) -> tuple[BenchmarkEpisodeResult, RuntimeDescription]: + reset_response = self._runtime_session.reset( + EpisodeResetRequest( + episode_id=episode.episode_id, + task_id=episode.task_id, + seed=episode.seed, + options={ + **dict(episode.options), + "task_index": episode.task_index, + "init_state_index": episode.init_state_index, + }, + ) + ) + self._robot_policy_module.reset(episode_id=episode.episode_id) + + runtime_description = reset_response.runtime_description + snapshot = self._runtime_session.latest_observation_snapshot() + if not snapshot.observed_streams: + raise RuntimeError("runtime session produced no observation streams after reset") + observations = snapshot.observations + reward_sum = 0.0 + done = False + success: bool | None = None + failure_reason: str | None = None + last_action: RobotPolicyAction | None = None + observed_streams = snapshot.observed_streams + video_frames: dict[str, list[np.ndarray]] = {stream: [] for stream in self._video_streams} + steps = 0 + + for tick_id in range(self._max_steps): + self._append_video_frames(video_frames, snapshot.values) + sample = self._sample_builder.build( + episode=episode, + tick_id=tick_id, + observations=observations, + runtime_description=runtime_description, + reward=reward_sum, + done=done, + success=success, + observation_values=snapshot.values, + ) + action = self._robot_policy_module.infer_action(sample) + frame = _runtime_action_frame(action, tick_id=tick_id) + step_response = self._runtime_session.step( + StepRequest( + episode_id=episode.episode_id, + tick_id=tick_id, + action=frame, + ) + ) + steps = tick_id + 1 + last_action = action + reward_sum += step_response.reward + done = step_response.done + success = step_response.success + snapshot = self._runtime_session.latest_observation_snapshot() + observations = snapshot.observations + observed_streams = snapshot.observed_streams + if done: + break + + episode_success = bool(success) + if not episode_success: + failure_reason = "done_without_success" if done else "max_steps_without_success" + + action_values = last_action.values if last_action is not None else () + self._write_videos(episode.episode_id, video_frames) + return BenchmarkEpisodeResult( + episode_id=episode.episode_id, + task_id=episode.task_id, + task_index=episode.task_index, + init_state_index=episode.init_state_index, + success=episode_success, + steps=steps, + reward_sum=reward_sum, + done=done, + failure_reason=failure_reason, + action_shape=(len(action_values),), + action_min=min(action_values) if action_values else None, + action_max=max(action_values) if action_values else None, + observed_streams=observed_streams, + ), runtime_description + + def _append_video_frames( + self, video_frames: dict[str, list[np.ndarray]], stream_values: Mapping[str, object] + ) -> None: + for stream in self._video_streams: + value = stream_values.get(stream) + if not isinstance(value, np.ndarray): + continue + if value.ndim != 3 or value.shape[2] != 3: + continue + video_frames[stream].append(value.astype(np.uint8, copy=False)) + + def _write_videos(self, episode_id: str, video_frames: dict[str, list[np.ndarray]]) -> None: + if self._video_dir is None: + return + for stream, frames in video_frames.items(): + if not frames: + continue + stream_name = stream.replace("/", "_") + video_path = self._video_dir / episode_id / f"{stream_name}.mp4" + video_path.parent.mkdir(parents=True, exist_ok=True) + imageio.mimsave(video_path, frames, fps=self._video_fps) # type: ignore[arg-type] + + def _summary(self, results: Sequence[BenchmarkEpisodeResult]) -> BenchmarkEvaluationSummary: + successes = sum(1 for result in results if result.success) + success_rate = successes / len(results) if results else 0.0 + return BenchmarkEvaluationSummary( + episodes=len(results), + successes=successes, + success_rate=success_rate, + success_threshold=self._success_threshold, + passed=success_rate > self._success_threshold, + ) + + def _write_artifacts( + self, + results: Sequence[BenchmarkEpisodeResult], + summary: BenchmarkEvaluationSummary, + runtime_description: RuntimeDescription | None, + ) -> None: + self._artifact_dir.mkdir(parents=True, exist_ok=True) + write_json(self._artifact_dir / "summary.json", _json_ready(summary)) + if runtime_description is not None: + write_json(self._artifact_dir / "runtime_description.json", runtime_description) + write_json( + self._artifact_dir / "checkpoint_metadata.json", + _json_ready(self._robot_policy_module.describe_backend()), + ) + _write_jsonl(self._artifact_dir / "episodes.jsonl", results) + + +def libero_object_episode_matrix( + *, init_state_indices: Sequence[int] = (0, 1, 2, 3, 4) +) -> list[BenchmarkEpisodeSpec]: + """Return the required 50-episode LIBERO object gate matrix.""" + + return [ + BenchmarkEpisodeSpec( + episode_id=f"libero_object_task{task_index}_init{init_state_index}", + task_id="libero_object", + task_index=task_index, + init_state_index=init_state_index, + ) + for task_index in range(10) + for init_state_index in init_state_indices + ] + + +def _runtime_action_frame(action: RobotPolicyAction, *, tick_id: int) -> RuntimeActionFrame: + return RuntimeActionFrame( + frame_type="runtime_action", + space_id=action.space_id, + values=list(action.values), + sequence=action.sequence, + tick_id=tick_id if action.sequence is None else None, + ) + + +def _write_jsonl(path: Path, rows: Sequence[object]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("".join(json.dumps(_json_ready(row), sort_keys=True) + "\n" for row in rows)) + + +def _json_ready(value: object) -> object: + if is_dataclass(value) and not isinstance(value, type): + return _json_ready(asdict(value)) + model_dump = getattr(value, "model_dump", None) + if callable(model_dump): + return model_dump(mode="json") + if isinstance(value, dict): + return {str(key): _json_ready(item) for key, item in value.items()} + if isinstance(value, tuple): + return [_json_ready(item) for item in value] + if isinstance(value, list): + return [_json_ready(item) for item in value] + return value diff --git a/dimos/robot_learning/policy_rollout/models.py b/dimos/robot_learning/policy_rollout/models.py new file mode 100644 index 0000000000..c75532bd20 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/models.py @@ -0,0 +1,150 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Mapping +from dataclasses import dataclass, field +from typing import TypeAlias + +JsonObject: TypeAlias = Mapping[str, object] +BackendPayload: TypeAlias = Mapping[str, object] +BackendActionOutput: TypeAlias = tuple[float, ...] | tuple[tuple[float, ...], ...] + + +@dataclass(frozen=True) +class RobotPolicyObservation: + """Runtime-independent policy observation for one inference step. + + Producers such as benchmark runners, simulators, replay loaders, or future + temporal observation assemblers populate semantically named observation roles. + Policy contracts then convert those roles into backend-specific batches. + """ + + observations: BackendPayload + timestamps: Mapping[str, float] = field(default_factory=dict) + metadata: JsonObject = field(default_factory=dict) + + +@dataclass(frozen=True) +class BackendBatch: + """Backend-ready policy inference batch produced by a robot contract.""" + + payload: BackendPayload + metadata: JsonObject = field(default_factory=dict) + + +@dataclass(frozen=True) +class BackendOutputEnvelope: + """Backend-native policy output and inference metadata.""" + + output: BackendActionOutput + metadata: JsonObject = field(default_factory=dict) + + +@dataclass(frozen=True) +class PolicyBackendDescription: + """Serializable metadata describing a loaded policy backend.""" + + backend_type: str + checkpoint_id: str | None = None + resolved_checkpoint_metadata: JsonObject = field(default_factory=dict) + device: str | None = None + policy_class: str | None = None + supports_episode_reset: bool = True + metadata: JsonObject = field(default_factory=dict) + + +@dataclass(frozen=True) +class RobotPolicyAction: + """Runtime-independent robot-learning policy action. + + Runtime adapters convert this action into benchmark runtime frames, control + task commands, or other execution-specific command surfaces. + """ + + space_id: str + values: tuple[float, ...] + sequence: int | None = None + metadata: JsonObject = field(default_factory=dict) + kind: str = "robot_policy_action" + + +@dataclass(frozen=True) +class RobotPolicyActionChunk: + """Runtime-independent ordered chunk of robot-learning policy actions.""" + + space_id: str + values: tuple[tuple[float, ...], ...] + sequence: int | None = None + timestamps: tuple[float, ...] | None = None + metadata: JsonObject = field(default_factory=dict) + kind: str = "robot_policy_action_chunk" + + def __post_init__(self) -> None: + if not self.values: + raise ValueError("RobotPolicyActionChunk must contain at least one action row") + action_dim = len(self.values[0]) + if action_dim == 0: + raise ValueError("RobotPolicyActionChunk action rows must not be empty") + if any(len(row) != action_dim for row in self.values): + raise ValueError("RobotPolicyActionChunk action rows must have consistent dimensions") + if self.timestamps is not None and len(self.timestamps) != len(self.values): + raise ValueError("RobotPolicyActionChunk timestamps must match horizon") + + @property + def horizon(self) -> int: + return len(self.values) + + @property + def action_dim(self) -> int: + return len(self.values[0]) + + @property + def shape(self) -> tuple[int, int]: + return (self.horizon, self.action_dim) + + def first_action(self) -> RobotPolicyAction: + return RobotPolicyAction( + space_id=self.space_id, + values=self.values[0], + sequence=self.sequence, + metadata=self.metadata, + ) + + +RuntimeActionOutput: TypeAlias = RobotPolicyAction | RobotPolicyActionChunk + + +@dataclass(frozen=True) +class RolloutEpisodeRecord: + """Per-episode rollout record for benchmark artifacts.""" + + episode_id: str + task_id: str + init_state_id: str + success: bool + steps: int + reward_sum: float = 0.0 + done: bool = False + failure_reason: str | None = None + metadata: JsonObject = field(default_factory=dict) + + +@dataclass(frozen=True) +class RolloutSummary: + """Aggregate rollout summary for benchmark artifacts.""" + + episodes: int + successes: int + success_rate: float + metadata: JsonObject = field(default_factory=dict) diff --git a/dimos/robot_learning/policy_rollout/registry.py b/dimos/robot_learning/policy_rollout/registry.py new file mode 100644 index 0000000000..bc243cc6bd --- /dev/null +++ b/dimos/robot_learning/policy_rollout/registry.py @@ -0,0 +1,142 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Lazy registries for robot policy rollout backends and contracts.""" + +from __future__ import annotations + +from collections.abc import Callable, Mapping +import importlib +import os +from typing import Generic, TypeVar, cast + +from dimos.robot_learning.policy_rollout.backends.backend import PolicyBackend +from dimos.robot_learning.policy_rollout.contract import RobotPolicyContract + +FactoryT = TypeVar("FactoryT") +BackendFactory = Callable[..., PolicyBackend] +ContractFactory = Callable[..., RobotPolicyContract] + + +class _LazyFactoryRegistry(Generic[FactoryT]): + def __init__( + self, + *, + package: str, + manifest_name: str, + manifest_attr: str, + item_label: str, + ) -> None: + self._package = package + self._manifest_name = manifest_name + self._manifest_attr = manifest_attr + self._item_label = item_label + self._factory_paths: dict[str, str] = {} + self._factories: dict[str, FactoryT] = {} + self.discover() + + def discover(self) -> None: + """Discover registry manifests without importing heavy implementations.""" + + package = importlib.import_module(self._package) + for root in package.__path__: + for entry in sorted(os.listdir(root)): + if entry.startswith(("_", ".")): + continue + entry_path = os.path.join(root, entry) + if not os.path.isdir(entry_path): + continue + module_name = f"{self._package}.{entry}.{self._manifest_name}" + try: + module = importlib.import_module(module_name) + except ModuleNotFoundError as exc: + if exc.name == module_name: + continue + raise + factories = getattr(module, self._manifest_attr, None) + if not isinstance(factories, Mapping): + raise TypeError(f"{module_name} must define {self._manifest_attr}") + for name, factory_path in factories.items(): + if not isinstance(name, str) or not isinstance(factory_path, str): + raise TypeError( + f"{module_name}.{self._manifest_attr} must map strings to strings" + ) + self.register_path(name, factory_path) + + def register_path(self, name: str, factory_path: str) -> None: + if ":" not in factory_path: + raise ValueError(f"Invalid {self._item_label} factory path: {factory_path!r}") + key = name.lower() + existing = self._factory_paths.get(key) + if existing is not None and existing != factory_path: + raise ValueError( + f"Duplicate {self._item_label} type {key!r}: {existing!r} vs {factory_path!r}" + ) + self._factory_paths[key] = factory_path + + def create(self, name: str, **params: object) -> FactoryT: + key = name.lower() + factory = self._resolve_factory(key) + callable_factory = cast("Callable[..., FactoryT]", factory) + return callable_factory(**params) + + def available(self) -> list[str]: + return sorted(self._factory_paths.keys()) + + def _resolve_factory(self, key: str) -> FactoryT: + if key in self._factories: + return self._factories[key] + if key not in self._factory_paths: + raise ValueError( + f"Unknown {self._item_label} type: {key!r}. Available: {self.available()}" + ) + factory_path = self._factory_paths[key] + module_name, attr = factory_path.split(":", maxsplit=1) + try: + module = importlib.import_module(module_name) + except ModuleNotFoundError as exc: + raise ValueError( + f"{self._item_label.title()} {key!r} is registered to missing module " + f"{module_name!r}" + ) from exc + factory = getattr(module, attr) + if not callable(factory): + raise TypeError(f"{self._item_label.title()} factory {factory_path!r} is not callable") + typed_factory = cast("FactoryT", factory) + self._factories[key] = typed_factory + return typed_factory + + +class PolicyBackendRegistry(_LazyFactoryRegistry[BackendFactory]): + def __init__(self) -> None: + super().__init__( + package="dimos.robot_learning.policy_rollout.backends", + manifest_name="__registry__", + manifest_attr="POLICY_BACKENDS", + item_label="policy backend", + ) + + +class RobotPolicyContractRegistry(_LazyFactoryRegistry[ContractFactory]): + def __init__(self) -> None: + super().__init__( + package="dimos.robot_learning.policy_rollout.contracts", + manifest_name="__registry__", + manifest_attr="ROBOT_POLICY_CONTRACTS", + item_label="robot policy contract", + ) + + +policy_backend_registry = PolicyBackendRegistry() +robot_policy_contract_registry = RobotPolicyContractRegistry() diff --git a/dimos/robot_learning/policy_rollout/robot_policy_module.py b/dimos/robot_learning/policy_rollout/robot_policy_module.py new file mode 100644 index 0000000000..0394981bea --- /dev/null +++ b/dimos/robot_learning/policy_rollout/robot_policy_module.py @@ -0,0 +1,254 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Callable +from dataclasses import dataclass, field +import threading +from typing import Any, Protocol, cast + +from pydantic import Field + +from dimos.core.core import rpc +from dimos.core.module import Module, ModuleConfig +from dimos.core.stream import In, Out +from dimos.robot_learning.policy_rollout.backends.backend import PolicyBackend +from dimos.robot_learning.policy_rollout.contract import RobotPolicyContract +from dimos.robot_learning.policy_rollout.models import ( + PolicyBackendDescription, + RobotPolicyAction, + RobotPolicyActionChunk, + RobotPolicyObservation, +) +from dimos.robot_learning.policy_rollout.registry import ( + policy_backend_registry, + robot_policy_contract_registry, +) +from dimos.spec.utils import Spec + + +class RobotPolicyModuleConfig(ModuleConfig): + """Config for registry-backed robot policy inference modules.""" + + backend_type: str = "lerobot" + backend_params: dict[str, object] = Field(default_factory=dict) + contract_type: str = "vla_jepa_libero" + contract_params: dict[str, object] = Field(default_factory=dict) + initialize_on_start: bool = False + + +@dataclass(frozen=True) +class PolicyChunkInferenceStatus: + """Status for live policy chunk inference triggers.""" + + accepted: bool + status: str + sequence: int | None = None + message: str | None = None + metadata: dict[str, object] = field(default_factory=dict) + + +class RobotPolicyChunkInferenceSpec(Spec, Protocol): + """RPC surface used by control tasks to request live policy chunk refill.""" + + def trigger_action_chunk_inference(self) -> PolicyChunkInferenceStatus: ... + + +class RobotPolicyModule(Module): + """Policy inference seam for robot-learning rollouts. + + The module owns backend lifecycle, episode reset, contract conversion, + backend inference, and action emission. Benchmark/runtime lifecycle, + sidecar reset/step, scoring, success gates, and artifact writing remain + outside this class. + """ + + config: RobotPolicyModuleConfig + policy_observation: In[RobotPolicyObservation] + policy_action_chunk: Out[RobotPolicyActionChunk] + + def __init__( + self, + backend: PolicyBackend | None = None, + contract: RobotPolicyContract | None = None, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) + self._backend: PolicyBackend = backend or cast( + "PolicyBackend", + policy_backend_registry.create(self.config.backend_type, **self.config.backend_params), + ) + self._contract: RobotPolicyContract = contract or cast( + "RobotPolicyContract", + robot_policy_contract_registry.create( + self.config.contract_type, **self.config.contract_params + ), + ) + self._initialized = False + self._last_action: RobotPolicyAction | None = None + self._last_action_chunk: RobotPolicyActionChunk | None = None + self._latest_observation: RobotPolicyObservation | None = None + self._backend_lock = threading.RLock() + self._live_lock = threading.Lock() + self._inference_in_flight = False + self._next_chunk_sequence = 0 + self._observation_unsubscribe: Callable[[], None] | None = None + + @property + def last_action(self) -> RobotPolicyAction | None: + return self._last_action + + @property + def last_action_chunk(self) -> RobotPolicyActionChunk | None: + return self._last_action_chunk + + @property + def latest_observation(self) -> RobotPolicyObservation | None: + return self._latest_observation + + @rpc + def start(self) -> None: + super().start() + self._observation_unsubscribe = self.policy_observation.subscribe(self.update_observation) + if self.config.initialize_on_start: + self.initialize() + + @rpc + def stop(self) -> None: + if self._observation_unsubscribe is not None: + self._observation_unsubscribe() + self._observation_unsubscribe = None + self.close() + super().stop() + + def initialize(self) -> None: + with self._backend_lock: + if self._initialized: + return + self._backend.initialize() + self._initialized = True + + @rpc + def reset_episode(self, episode_id: str | None = None) -> None: + del episode_id + with self._backend_lock: + self.initialize() + self._backend.reset_episode() + self._last_action = None + self._last_action_chunk = None + + @rpc + def reset(self, episode_id: str | None = None) -> None: + self.reset_episode(episode_id=episode_id) + + @rpc + def infer_action(self, sample: RobotPolicyObservation) -> RobotPolicyAction: + with self._backend_lock: + self.initialize() + batch = self._contract.to_backend_batch(sample) + backend_output = self._backend.infer_batch(batch) + action = self._contract.from_backend_output(backend_output) + return self.emit_action(action) + + @rpc + def infer_action_chunk(self, sample: RobotPolicyObservation) -> RobotPolicyActionChunk: + chunk = self._infer_action_chunk_from_observation(sample) + return self.emit_action_chunk(chunk) + + @rpc + def update_observation(self, observation: RobotPolicyObservation) -> None: + with self._live_lock: + self._latest_observation = observation + + @rpc + def trigger_action_chunk_inference(self) -> PolicyChunkInferenceStatus: + with self._live_lock: + observation = self._latest_observation + if observation is None: + return PolicyChunkInferenceStatus( + accepted=False, + status="not_ready", + message="no live robot policy observation has been received", + ) + if self._inference_in_flight: + return PolicyChunkInferenceStatus( + accepted=False, + status="already_in_flight", + sequence=self._next_chunk_sequence - 1 + if self._next_chunk_sequence > 0 + else None, + ) + sequence = self._next_chunk_sequence + self._next_chunk_sequence += 1 + self._inference_in_flight = True + + thread = threading.Thread( + target=self._run_action_chunk_inference, + args=(observation, sequence), + name=f"RobotPolicyModule-ChunkInference-{sequence}", + daemon=True, + ) + thread.start() + return PolicyChunkInferenceStatus( + accepted=True, + status="started", + sequence=sequence, + ) + + def emit_action(self, action: RobotPolicyAction) -> RobotPolicyAction: + self._last_action = action + return action + + def emit_action_chunk(self, chunk: RobotPolicyActionChunk) -> RobotPolicyActionChunk: + self._last_action_chunk = chunk + self.policy_action_chunk.publish(chunk) + return chunk + + def _run_action_chunk_inference( + self, observation: RobotPolicyObservation, sequence: int + ) -> None: + try: + chunk = self._infer_action_chunk_from_observation(observation) + if chunk.sequence is None: + chunk = RobotPolicyActionChunk( + space_id=chunk.space_id, + values=chunk.values, + sequence=sequence, + timestamps=chunk.timestamps, + metadata=chunk.metadata, + ) + self.emit_action_chunk(chunk) + finally: + with self._live_lock: + self._inference_in_flight = False + + def _infer_action_chunk_from_observation( + self, observation: RobotPolicyObservation + ) -> RobotPolicyActionChunk: + with self._backend_lock: + self.initialize() + batch = self._contract.to_backend_batch(observation) + backend_output = self._backend.infer_batch(batch) + return self._contract.chunk_from_backend_output(backend_output) + + def close(self) -> None: + with self._backend_lock: + self._backend.close() + self._initialized = False + with self._live_lock: + self._inference_in_flight = False + self._close_module() + + @rpc + def describe_backend(self) -> PolicyBackendDescription: + return self._backend.describe() diff --git a/dimos/robot_learning/policy_rollout/test_evaluation.py b/dimos/robot_learning/policy_rollout/test_evaluation.py new file mode 100644 index 0000000000..a8c572526c --- /dev/null +++ b/dimos/robot_learning/policy_rollout/test_evaluation.py @@ -0,0 +1,366 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from dataclasses import dataclass, field +import json +from pathlib import Path + +from dimos_runtime_protocol import ( + EpisodeResetRequest, + EpisodeResetResponse, + MotorStateFrame, + ObservationFrame, + ObservationKind, + RuntimeDescription, + StepRequest, + StepResponse, +) +import numpy as np +import pytest +from pytest_mock import MockerFixture + +from dimos.robot_learning.policy_rollout.evaluation import ( + BenchmarkEpisodeSpec, + BenchmarkPolicyEvalModule, + BenchmarkPolicyEvalRunner, + PolicyEvalRuntimeSession, + RuntimeStreamSnapshot, + lerobot_libero_policy_eval_blueprint, +) +from dimos.robot_learning.policy_rollout.models import ( + PolicyBackendDescription, + RobotPolicyAction, + RobotPolicyObservation, +) +from dimos.robot_learning.policy_rollout.robot_policy_module import RobotPolicyModule + + +@dataclass +class FakeRuntimeSession(PolicyEvalRuntimeSession): + success_by_episode: dict[str, bool] + missing_reset_streams: bool = False + reset_requests: list[EpisodeResetRequest] = field(default_factory=list) + step_requests: list[StepRequest] = field(default_factory=list) + latest_snapshot: RuntimeStreamSnapshot = field(default_factory=RuntimeStreamSnapshot) + + def reset(self, request: EpisodeResetRequest) -> EpisodeResetResponse: + self.reset_requests.append(request) + self.latest_snapshot = ( + RuntimeStreamSnapshot() if self.missing_reset_streams else _snapshot("agentview") + ) + return EpisodeResetResponse( + episode_id=request.episode_id, + runtime_description=_runtime_description(), + observations=[], + ) + + def step(self, request: StepRequest) -> StepResponse: + self.step_requests.append(request) + success = self.success_by_episode[request.episode_id] + self.latest_snapshot = _snapshot("agentview", "eye_in_hand") + return StepResponse( + episode_id=request.episode_id, + tick_id=request.tick_id, + motor_state=MotorStateFrame(robot_id="panda", names=[], q=[], dq=[], tau=[]), + observations=[], + reward=1.0 if success else 0.0, + done=True, + success=success, + ) + + def latest_observation_snapshot(self) -> RuntimeStreamSnapshot: + return self.latest_snapshot + + +@dataclass +class FakeRobotPolicyModule: + fail_on_infer: bool = False + reset_episode_ids: list[str | None] = field(default_factory=list) + samples: list[RobotPolicyObservation] = field(default_factory=list) + closed: bool = False + + def reset(self, episode_id: str | None = None) -> None: + self.reset_episode_ids.append(episode_id) + + def infer_action(self, sample: RobotPolicyObservation) -> RobotPolicyAction: + if self.fail_on_infer: + raise ValueError("contract mismatch") + self.samples.append(sample) + return RobotPolicyAction( + space_id="libero.ee_delta_6d_gripper.normalized.v1", + values=(0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0), + ) + + def close(self) -> None: + self.closed = True + + def describe_backend(self) -> PolicyBackendDescription: + return PolicyBackendDescription( + backend_type="fake", checkpoint_id="lerobot/VLA-JEPA-LIBERO" + ) + + +def test_runner_owns_runtime_reset_step_policy_reset_and_artifacts(tmp_path: Path) -> None: + runtime = FakeRuntimeSession(success_by_episode={"ep-0": True}) + policy = FakeRobotPolicyModule() + runner = BenchmarkPolicyEvalRunner( + runtime_session=runtime, + robot_policy_module=policy, # type: ignore[arg-type] + artifact_dir=tmp_path, + max_steps=3, + ) + + summary = runner.run( + [ + BenchmarkEpisodeSpec( + episode_id="ep-0", + task_id="libero_object", + task_index=0, + init_state_index=1, + seed=7, + ) + ] + ) + + assert summary.episodes == 1 + assert summary.successes == 1 + assert summary.passed + assert policy.reset_episode_ids == ["ep-0"] + assert policy.closed + assert runtime.reset_requests[0].options["task_index"] == 0 + assert runtime.reset_requests[0].options["init_state_index"] == 1 + assert len(runtime.step_requests) == 1 + action = runtime.step_requests[0].action + assert action.frame_type == "runtime_action" + assert action.tick_id == 0 + assert action.space_id == "libero.ee_delta_6d_gripper.normalized.v1" + assert "agentview" in policy.samples[0].observations + assert policy.samples[0].metadata["language"] == "pick up the object" + + summary_json = json.loads((tmp_path / "summary.json").read_text()) + assert summary_json["success_rate"] == 1.0 + assert (tmp_path / "runtime_description.json").exists() + assert not (tmp_path / "contract_description.json").exists() + assert (tmp_path / "checkpoint_metadata.json").exists() + episode_records = (tmp_path / "episodes.jsonl").read_text().splitlines() + assert len(episode_records) == 1 + episode_record = json.loads(episode_records[0]) + assert episode_record["action_shape"] == [7] + assert episode_record["observed_streams"] == ["agentview", "eye_in_hand"] + + +def test_runner_continues_after_policy_episode_failure(tmp_path: Path) -> None: + runtime = FakeRuntimeSession(success_by_episode={"ep-0": False, "ep-1": True}) + policy = FakeRobotPolicyModule() + runner = BenchmarkPolicyEvalRunner( + runtime_session=runtime, + robot_policy_module=policy, # type: ignore[arg-type] + artifact_dir=tmp_path, + max_steps=1, + ) + + summary = runner.run( + [ + BenchmarkEpisodeSpec("ep-0", "libero_object", 0, 0), + BenchmarkEpisodeSpec("ep-1", "libero_object", 1, 0), + ] + ) + + assert summary.episodes == 2 + assert summary.successes == 1 + assert not summary.passed + assert [request.episode_id for request in runtime.reset_requests] == ["ep-0", "ep-1"] + assert [request.episode_id for request in runtime.step_requests] == ["ep-0", "ep-1"] + episode_records = [ + json.loads(line) for line in (tmp_path / "episodes.jsonl").read_text().splitlines() + ] + assert episode_records[0]["failure_reason"] == "done_without_success" + assert episode_records[1]["success"] is True + + +def test_runner_aborts_setup_or_contract_error(tmp_path: Path) -> None: + runtime = FakeRuntimeSession(success_by_episode={"ep-0": True}) + policy = FakeRobotPolicyModule(fail_on_infer=True) + runner = BenchmarkPolicyEvalRunner( + runtime_session=runtime, + robot_policy_module=policy, # type: ignore[arg-type] + artifact_dir=tmp_path, + max_steps=1, + ) + + with pytest.raises(ValueError, match="contract mismatch"): + runner.run([BenchmarkEpisodeSpec("ep-0", "libero_object", 0, 0)]) + + assert len(runtime.step_requests) == 0 + assert policy.closed + + +def test_runner_fails_when_runtime_session_has_no_stream_snapshot(tmp_path: Path) -> None: + runtime = FakeRuntimeSession(success_by_episode={"ep-0": True}, missing_reset_streams=True) + policy = FakeRobotPolicyModule() + runner = BenchmarkPolicyEvalRunner( + runtime_session=runtime, + robot_policy_module=policy, # type: ignore[arg-type] + artifact_dir=tmp_path, + max_steps=1, + ) + + with pytest.raises(RuntimeError, match="no observation streams"): + runner.run([BenchmarkEpisodeSpec("ep-0", "libero_object", 0, 0)]) + + assert len(runtime.step_requests) == 0 + assert policy.closed + + +def test_runner_writes_video_frames_when_enabled(tmp_path: Path, mocker: MockerFixture) -> None: + video_writer = mocker.patch("dimos.robot_learning.policy_rollout.evaluation.imageio.mimsave") + runtime = FakeRuntimeSession(success_by_episode={"ep-0": True}) + policy = FakeRobotPolicyModule() + runner = BenchmarkPolicyEvalRunner( + runtime_session=runtime, + robot_policy_module=policy, # type: ignore[arg-type] + artifact_dir=tmp_path, + max_steps=1, + video_dir=tmp_path / "videos", + video_streams=("agentview",), + video_fps=12, + ) + + runner.run([BenchmarkEpisodeSpec("ep-0", "libero_object", 0, 0)]) + + video_writer.assert_called_once() + assert video_writer.call_args.args[0] == tmp_path / "videos" / "ep-0" / "agentview.mp4" + assert len(video_writer.call_args.args[1]) == 1 + assert video_writer.call_args.kwargs["fps"] == 12 + + +def test_module_run_episodes_uses_injected_clients_and_writes_artifacts( + tmp_path: Path, +) -> None: + runtime = FakeRuntimeSession(success_by_episode={"ep-0": True}) + policy = FakeRobotPolicyModule() + module = BenchmarkPolicyEvalModule( + runtime_session=runtime, + robot_policy_module=policy, # type: ignore[arg-type] + artifact_dir=str(tmp_path), + max_steps=2, + ) + + try: + summary = module.run_episodes([BenchmarkEpisodeSpec("ep-0", "libero_object", 0, 1, seed=7)]) + + assert summary.episodes == 1 + assert summary.successes == 1 + assert summary.passed + assert len(module.last_results) == 1 + assert module.last_results[0].episode_id == "ep-0" + assert policy.reset_episode_ids == ["ep-0"] + assert not policy.closed + assert runtime.reset_requests[0].options["task_index"] == 0 + assert runtime.reset_requests[0].options["init_state_index"] == 1 + assert len(runtime.step_requests) == 1 + assert (tmp_path / "summary.json").exists() + assert (tmp_path / "runtime_description.json").exists() + assert not (tmp_path / "contract_description.json").exists() + assert (tmp_path / "checkpoint_metadata.json").exists() + episode_records = (tmp_path / "episodes.jsonl").read_text().splitlines() + assert len(episode_records) == 1 + assert json.loads(episode_records[0])["success"] is True + finally: + module.stop() + + +def test_module_run_episodes_uses_configured_clients(tmp_path: Path) -> None: + runtime = FakeRuntimeSession(success_by_episode={"ep-0": True}) + policy = FakeRobotPolicyModule() + module = BenchmarkPolicyEvalModule(artifact_dir=str(tmp_path), max_steps=1) + + try: + module.configure( + runtime_session=runtime, + robot_policy_module=policy, # type: ignore[arg-type] + ) + summary = module.run_episodes([BenchmarkEpisodeSpec("ep-0", "libero_object", 0, 0)]) + + assert summary.successes == 1 + assert len(module.last_results) == 1 + assert len(runtime.step_requests) == 1 + assert not policy.closed + finally: + module.stop() + + +def test_module_run_episodes_requires_runtime_and_policy_clients( + tmp_path: Path, +) -> None: + module = BenchmarkPolicyEvalModule(artifact_dir=str(tmp_path)) + + try: + with pytest.raises(RuntimeError, match="requires runtime session and policy client"): + module.run_episodes([BenchmarkEpisodeSpec("ep-0", "libero_object", 0, 0)]) + finally: + module.stop() + + +def test_lerobot_libero_policy_eval_blueprint_configures_modules() -> None: + blueprint = lerobot_libero_policy_eval_blueprint( + checkpoint_id="checkpoint", + device="cpu", + artifact_dir="artifacts/out", + max_steps=12, + success_threshold=0.6, + ) + + atoms_by_module = {atom.module: atom for atom in blueprint.blueprints} + policy_atom = atoms_by_module[RobotPolicyModule] + eval_atom = atoms_by_module[BenchmarkPolicyEvalModule] + + assert policy_atom.kwargs["backend_type"] == "lerobot" + assert policy_atom.kwargs["backend_params"] == { + "checkpoint_id": "checkpoint", + "device": "cpu", + } + assert policy_atom.kwargs["contract_type"] == "vla_jepa_libero" + assert eval_atom.kwargs["artifact_dir"] == "artifacts/out" + assert eval_atom.kwargs["max_steps"] == 12 + assert eval_atom.kwargs["success_threshold"] == 0.6 + + +def _runtime_description() -> RuntimeDescription: + return RuntimeDescription( + runtime_id="libero-pro", + backend="libero-pro", + capabilities=["native-runtime-action"], + robot_surfaces=[], + control_step_hz=20, + observation_streams=["agentview", "eye_in_hand", "robot_state"], + metadata={"language": "pick up the object"}, + ) + + +def _snapshot(*streams: str) -> RuntimeStreamSnapshot: + values = {stream: np.zeros((4, 4, 3), dtype=np.uint8) for stream in streams} + return RuntimeStreamSnapshot( + observations=tuple(_observation(stream) for stream in streams), + values=values, + ) + + +def _observation(stream: str) -> ObservationFrame: + return ObservationFrame( + stream=stream, + kind=ObservationKind.IMAGE, + shape=[128, 128, 3], + dtype="uint8", + ) diff --git a/dimos/robot_learning/policy_rollout/test_lerobot_backend.py b/dimos/robot_learning/policy_rollout/test_lerobot_backend.py new file mode 100644 index 0000000000..996dd84789 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/test_lerobot_backend.py @@ -0,0 +1,290 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from types import SimpleNamespace +from typing import Any, cast + +import numpy as np +import pytest + +from dimos.robot_learning.policy_rollout.backends.lerobot.backend import ( + LeRobotBackend, + _tensorized_preprocessor_input, +) +from dimos.robot_learning.policy_rollout.models import BackendBatch + + +class FakeNoGrad: + def __init__(self) -> None: + self.entered = False + + def __enter__(self) -> None: + self.entered = True + + def __exit__(self, exc_type: object, exc: object, traceback: object) -> None: + return None + + +class FakePolicy: + def __init__(self) -> None: + self.config = SimpleNamespace(device="cpu", n_action_steps=7, action_dim=7) + self.eval_called = False + self.reset_called = False + self.selected_batches: list[object] = [] + self.chunk_batches: list[object] = [] + self.to_devices: list[str] = [] + + def eval(self) -> None: + self.eval_called = True + + def to(self, device: str) -> None: + self.to_devices.append(device) + + def reset(self) -> None: + self.reset_called = True + + def select_action(self, batch: object) -> tuple[float, ...]: + self.selected_batches.append(batch) + return (0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0) + + def predict_action_chunk(self, batch: object) -> object: + self.chunk_batches.append(batch) + return ((0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0),) + + +class FakeBatchedChunkPolicy(FakePolicy): + def predict_action_chunk(self, batch: object) -> np.ndarray: + self.chunk_batches.append(batch) + return np.asarray( + [[[0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0]]], + dtype=np.float32, + ) + + +class FakePolicyClass: + policy = FakePolicy() + checkpoint_ids: list[str] = [] + + @classmethod + def from_pretrained(cls, pretrained_name_or_path: str) -> FakePolicy: + cls.checkpoint_ids.append(pretrained_name_or_path) + return cls.policy + + +def test_lerobot_backend_import_boundary_without_lerobot() -> None: + backend = LeRobotBackend() + + description = backend.describe() + + assert description.backend_type == "lerobot" + assert description.checkpoint_id == "lerobot/VLA-JEPA-LIBERO" + + +def test_initialize_loads_policy_sets_device_eval_and_processors(mocker) -> None: + policy = FakePolicy() + FakePolicyClass.policy = policy + FakePolicyClass.checkpoint_ids = [] + preprocessor_calls: list[object] = [] + postprocessor_calls: list[object] = [] + factory_calls: list[dict[str, object]] = [] + + def make_pre_post_processors(spec: object, **kwargs: object) -> tuple[object, object]: + del spec + factory_calls.append(dict(kwargs)) + return ( + lambda batch: _record(preprocessor_calls, batch), + lambda output: _record(postprocessor_calls, output), + ) + + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._load_policy_class", + return_value=FakePolicyClass, + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._make_pre_post_processors", + side_effect=make_pre_post_processors, + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._torch_no_grad", + return_value=FakeNoGrad(), + ) + backend = LeRobotBackend(checkpoint_id="lerobot/VLA-JEPA-LIBERO", device="cpu") + + backend.initialize() + output = backend.infer_batch(BackendBatch(payload={"observation.state": "state"})) + backend.reset_episode() + description = backend.describe() + + assert FakePolicyClass.checkpoint_ids == ["lerobot/VLA-JEPA-LIBERO"] + assert factory_calls == [ + { + "policy_cfg": policy.config, + "pretrained_path": "lerobot/VLA-JEPA-LIBERO", + "preprocessor_overrides": {"device_processor": {"device": "cpu"}}, + } + ] + assert policy.eval_called + assert policy.to_devices == ["cpu"] + assert policy.reset_called + assert preprocessor_calls == [{"observation.state": "state"}] + assert postprocessor_calls == [(0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0)] + assert output.output == pytest.approx((0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0)) + assert output.metadata["inference_method"] == "select_action" + assert output.metadata["policy_family"] == "vla_jepa" + assert output.metadata["output_shape"] == [7] + assert description.policy_class is not None + assert description.device == "cpu" + assert description.metadata["processor_source"] == "checkpoint" + assert description.metadata["policy_family"] == "vla_jepa" + assert description.metadata["configured_policy_class"] == ( + "lerobot.policies.vla_jepa.modeling_vla_jepa.VLAJEPAPolicy" + ) + assert description.metadata["supports_action_chunk_inference"] is True + + +def test_lerobot_backend_can_select_xvla_policy_family(mocker) -> None: + policy = FakePolicy() + FakePolicyClass.policy = policy + FakePolicyClass.checkpoint_ids = [] + + load_policy = mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._load_policy_class", + return_value=FakePolicyClass, + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._make_pre_post_processors", + return_value=(lambda batch: batch, lambda output: output), + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._torch_no_grad", + return_value=FakeNoGrad(), + ) + backend = LeRobotBackend(policy_family="xvla", device="cpu", use_action_chunk=True) + + output = backend.infer_batch(BackendBatch(payload={"observation.state": "state"})) + description = backend.describe() + + family_spec = load_policy.call_args.args[0] + assert family_spec.family == "xvla" + assert FakePolicyClass.checkpoint_ids == ["lerobot/xvla-libero"] + assert policy.chunk_batches == [{"observation.state": "state"}] + assert output.metadata["policy_family"] == "xvla" + assert output.metadata["checkpoint_id"] == "lerobot/xvla-libero" + assert description.checkpoint_id == "lerobot/xvla-libero" + assert description.metadata["policy_family"] == "xvla" + assert description.metadata["configured_policy_class"] == ( + "lerobot.policies.xvla.modeling_xvla.XVLAPolicy" + ) + assert description.metadata["inference_method"] == "predict_action_chunk" + assert description.metadata["supports_action_chunk_inference"] is True + + +def test_lerobot_backend_can_route_to_action_chunk(mocker) -> None: + policy = FakePolicy() + FakePolicyClass.policy = policy + FakePolicyClass.checkpoint_ids = [] + + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._load_policy_class", + return_value=FakePolicyClass, + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._make_pre_post_processors", + return_value=(lambda batch: batch, lambda output: output), + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._torch_no_grad", + return_value=FakeNoGrad(), + ) + backend = LeRobotBackend(use_action_chunk=True) + + output = backend.infer_batch(BackendBatch(payload={"observation.state": "state"})) + + assert policy.selected_batches == [] + assert policy.chunk_batches == [{"observation.state": "state"}] + assert output.metadata["inference_method"] == "predict_action_chunk" + assert output.metadata["output_shape"] == [1, 7] + assert len(output.output) == 1 + assert output.output[0] == pytest.approx((0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0)) + + +def test_lerobot_backend_accepts_singleton_batch_action_chunk(mocker) -> None: + policy = FakeBatchedChunkPolicy() + FakePolicyClass.policy = policy + FakePolicyClass.checkpoint_ids = [] + + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._load_policy_class", + return_value=FakePolicyClass, + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._make_pre_post_processors", + return_value=(lambda batch: batch, lambda output: output), + ) + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._torch_no_grad", + return_value=FakeNoGrad(), + ) + backend = LeRobotBackend(use_action_chunk=True) + + output = backend.infer_batch(BackendBatch(payload={"observation.state": "state"})) + + assert output.metadata["output_shape"] == [1, 1, 7] + assert len(output.output) == 1 + assert output.output[0] == pytest.approx((0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0)) + + +def test_lerobot_backend_tensorizes_numpy_inputs_before_lerobot_preprocessor() -> None: + torch_module = cast("Any", pytest.importorskip("torch")) + image = np.full((2, 3, 3), 255, dtype=np.uint8) + state = np.zeros((8,), dtype=np.float32) + + prepared = _tensorized_preprocessor_input( + { + "observation.images.image": image, + "observation.state": state, + "task": "pick up the object", + } + ) + + prepared_image = cast("Any", prepared["observation.images.image"]) + prepared_state = cast("Any", prepared["observation.state"]) + assert isinstance(prepared_image, torch_module.Tensor) + assert prepared_image.shape == (3, 2, 3) + assert prepared_image.dtype == torch_module.float32 + assert torch_module.max(prepared_image) == 1.0 + assert isinstance(prepared_state, torch_module.Tensor) + assert prepared_state.shape == (8,) + assert prepared["task"] == "pick up the object" + + +def test_lerobot_backend_missing_dependency_error(mocker) -> None: + mocker.patch( + "dimos.robot_learning.policy_rollout.backends.lerobot.backend._load_policy_class", + side_effect=RuntimeError("Install LeRobot from GitHub main with the xvla extra"), + ) + backend = LeRobotBackend(policy_family="xvla") + + with pytest.raises(RuntimeError, match="xvla extra"): + backend.initialize() + + +def test_lerobot_backend_rejects_unknown_policy_family() -> None: + with pytest.raises(ValueError, match="unsupported LeRobot policy family"): + LeRobotBackend(policy_family="unknown") + + +def _record(calls: list[object], value: object) -> object: + calls.append(value) + return value diff --git a/dimos/robot_learning/policy_rollout/test_models.py b/dimos/robot_learning/policy_rollout/test_models.py new file mode 100644 index 0000000000..91c40cfd56 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/test_models.py @@ -0,0 +1,83 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from dataclasses import asdict + +import pytest + +from dimos.robot_learning.policy_rollout.models import ( + RobotPolicyAction, + RobotPolicyActionChunk, + RobotPolicyObservation, +) + + +def test_robot_policy_observation_is_runtime_independent_dataclass() -> None: + observation = RobotPolicyObservation( + observations={"agentview": "image", "robot_state": [0.0] * 8}, + timestamps={"agentview": 1.25}, + metadata={"source": "test", "language": "pick up the object"}, + ) + + payload = asdict(observation) + + assert payload["observations"]["robot_state"] == [0.0] * 8 + assert payload["metadata"]["language"] == "pick up the object" + assert payload["timestamps"] == {"agentview": 1.25} + + +def test_robot_policy_action_is_runtime_independent_dataclass() -> None: + action = RobotPolicyAction( + space_id="libero.ee_delta_6d_gripper.normalized.v1", + values=(0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0), + metadata={"backend": "fake"}, + ) + + payload = asdict(action) + + assert payload["kind"] == "robot_policy_action" + assert payload["space_id"] == "libero.ee_delta_6d_gripper.normalized.v1" + assert payload["values"] == (0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0) + + +def test_robot_policy_action_chunk_exposes_shape_and_first_action() -> None: + chunk = RobotPolicyActionChunk( + space_id="libero.ee_delta_6d_gripper.normalized.v1", + values=((0.0, 0.1), (0.2, 0.3)), + sequence=3, + timestamps=(1.0, 1.1), + metadata={"backend": "fake"}, + ) + + assert chunk.kind == "robot_policy_action_chunk" + assert chunk.horizon == 2 + assert chunk.action_dim == 2 + assert chunk.shape == (2, 2) + assert chunk.first_action() == RobotPolicyAction( + space_id="libero.ee_delta_6d_gripper.normalized.v1", + values=(0.0, 0.1), + sequence=3, + metadata={"backend": "fake"}, + ) + + +def test_robot_policy_action_chunk_rejects_empty_or_ragged_values() -> None: + with pytest.raises(ValueError, match="at least one"): + RobotPolicyActionChunk(space_id="space", values=()) + with pytest.raises(ValueError, match="must not be empty"): + RobotPolicyActionChunk(space_id="space", values=((),)) + with pytest.raises(ValueError, match="consistent dimensions"): + RobotPolicyActionChunk(space_id="space", values=((0.0,), (0.1, 0.2))) + with pytest.raises(ValueError, match="timestamps"): + RobotPolicyActionChunk(space_id="space", values=((0.0,),), timestamps=(1.0, 1.1)) diff --git a/dimos/robot_learning/policy_rollout/test_registry.py b/dimos/robot_learning/policy_rollout/test_registry.py new file mode 100644 index 0000000000..5723af130d --- /dev/null +++ b/dimos/robot_learning/policy_rollout/test_registry.py @@ -0,0 +1,71 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +import pytest + +from dimos.robot_learning.policy_rollout.backends.lerobot.backend import LeRobotBackend +from dimos.robot_learning.policy_rollout.registry import ( + PolicyBackendRegistry, + RobotPolicyContractRegistry, +) +from dimos.robot_learning.policy_rollout.vla_jepa_libero_contract import ( + VlaJepaLiberoRobotContract, +) +from dimos.robot_learning.policy_rollout.xvla_libero_contract import XvlaLiberoRobotContract + + +def test_backend_registry_discovers_lerobot_without_importing_lerobot_policy() -> None: + lerobot_policy_modules_before = { + module_name for module_name in sys.modules if module_name.startswith("lerobot.policies") + } + registry = PolicyBackendRegistry() + + assert "lerobot" in registry.available() + assert { + module_name for module_name in sys.modules if module_name.startswith("lerobot.policies") + } == lerobot_policy_modules_before + + +def test_backend_registry_creates_lerobot_backend() -> None: + backend = PolicyBackendRegistry().create( + "lerobot", checkpoint_id="lerobot/VLA-JEPA-LIBERO", device="cpu" + ) + + assert isinstance(backend, LeRobotBackend) + assert backend.describe().checkpoint_id == "lerobot/VLA-JEPA-LIBERO" + assert backend.describe().device == "cpu" + + +def test_contract_registry_creates_vla_jepa_libero_contract() -> None: + contract = RobotPolicyContractRegistry().create("vla_jepa_libero") + + assert isinstance(contract, VlaJepaLiberoRobotContract) + + +def test_contract_registry_creates_xvla_libero_contract() -> None: + contract = RobotPolicyContractRegistry().create("xvla_libero") + + assert isinstance(contract, XvlaLiberoRobotContract) + + +def test_registry_unknown_type_and_duplicate_path_errors() -> None: + registry = PolicyBackendRegistry() + + with pytest.raises(ValueError, match="Unknown policy backend type"): + registry.create("missing") + + with pytest.raises(ValueError, match="Duplicate policy backend type"): + registry.register_path("lerobot", "somewhere.else:create_backend") diff --git a/dimos/robot_learning/policy_rollout/test_robot_policy_module.py b/dimos/robot_learning/policy_rollout/test_robot_policy_module.py new file mode 100644 index 0000000000..0ef0a3ce80 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/test_robot_policy_module.py @@ -0,0 +1,238 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from dataclasses import dataclass, field +import threading +from typing import cast + +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + PolicyBackendDescription, + RobotPolicyAction, + RobotPolicyActionChunk, + RobotPolicyObservation, +) +from dimos.robot_learning.policy_rollout.robot_policy_module import ( + RobotPolicyModule, + RobotPolicyModuleConfig, +) + + +@dataclass +class FakeBackend: + initialized: bool = False + reset_count: int = 0 + closed: bool = False + block_infer: threading.Event | None = None + batches: list[BackendBatch] = field(default_factory=list) + + def initialize(self) -> None: + self.initialized = True + + def reset_episode(self) -> None: + self.reset_count += 1 + + def infer_batch(self, batch: BackendBatch) -> BackendOutputEnvelope: + if self.block_infer is not None: + self.block_infer.wait(timeout=2.0) + self.batches.append(batch) + return BackendOutputEnvelope(output=(0.1, 0.2, 0.3), metadata={"backend": "fake"}) + + def close(self) -> None: + self.closed = True + + def describe(self) -> PolicyBackendDescription: + return PolicyBackendDescription(backend_type="fake", checkpoint_id="fake-checkpoint") + + +@dataclass +class FakeContract: + samples: list[RobotPolicyObservation] = field(default_factory=list) + outputs: list[BackendOutputEnvelope] = field(default_factory=list) + + def to_backend_batch(self, sample: RobotPolicyObservation) -> BackendBatch: + self.samples.append(sample) + return BackendBatch(payload={"observations": dict(sample.observations)}) + + def from_backend_output(self, output: BackendOutputEnvelope) -> RobotPolicyAction: + self.outputs.append(output) + assert isinstance(output.output, tuple) + assert not isinstance(output.output[0], tuple) + values = cast("tuple[float, ...]", output.output) + return RobotPolicyAction(space_id="fake.action.v1", values=values) + + def chunk_from_backend_output(self, output: BackendOutputEnvelope) -> RobotPolicyActionChunk: + self.outputs.append(output) + assert isinstance(output.output, tuple) + if output.output and isinstance(output.output[0], tuple): + values = cast("tuple[tuple[float, ...], ...]", output.output) + else: + values = (cast("tuple[float, ...]", output.output),) + return RobotPolicyActionChunk(space_id="fake.action.v1", values=values) + + +def test_infer_action_uses_contract_backend_and_emits_action() -> None: + backend = FakeBackend() + contract = FakeContract() + module = RobotPolicyModule(backend=backend, contract=contract) + sample = RobotPolicyObservation(observations={"id": "obs-1"}) + + try: + action = module.infer_action(sample) + + assert backend.initialized + assert contract.samples == [sample] + assert backend.batches == [BackendBatch(payload={"observations": {"id": "obs-1"}})] + assert len(contract.outputs) == 1 + assert action == RobotPolicyAction(space_id="fake.action.v1", values=(0.1, 0.2, 0.3)) + assert module.last_action == action + finally: + module.close() + + +def test_public_reset_resets_backend_episode_state_and_clears_last_action() -> None: + backend = FakeBackend() + contract = FakeContract() + module = RobotPolicyModule(backend=backend, contract=contract) + try: + module.infer_action(RobotPolicyObservation(observations={"id": "obs-1"})) + + module.reset_episode(episode_id="episode-2") + + assert backend.initialized + assert backend.reset_count == 1 + assert module.last_action is None + finally: + module.close() + + +def test_backend_description_and_close_delegate_to_seams() -> None: + backend = FakeBackend() + contract = FakeContract() + module = RobotPolicyModule(backend=backend, contract=contract) + try: + assert module.describe_backend().backend_type == "fake" + + module.close() + + assert backend.closed + finally: + module.close() + + +def test_module_config_defaults_are_registry_names() -> None: + module = RobotPolicyModuleConfig() + + assert module.backend_type == "lerobot" + assert module.contract_type == "vla_jepa_libero" + + +def test_update_observation_stores_latest_live_policy_observation() -> None: + module = RobotPolicyModule(backend=FakeBackend(), contract=FakeContract()) + observation = RobotPolicyObservation(observations={"id": "obs-1"}) + + try: + module.update_observation(observation) + + assert module.latest_observation == observation + finally: + module.close() + + +def test_infer_action_chunk_publishes_chunk_and_preserves_sync_action() -> None: + backend = FakeBackend() + contract = FakeContract() + module = RobotPolicyModule(backend=backend, contract=contract) + published: list[RobotPolicyActionChunk] = [] + module.policy_action_chunk.subscribe(published.append) + + try: + chunk = module.infer_action_chunk(RobotPolicyObservation(observations={"id": "obs-1"})) + action = module.infer_action(RobotPolicyObservation(observations={"id": "obs-2"})) + + assert chunk.shape == (1, 3) + assert published == [chunk] + assert module.last_action_chunk == chunk + assert action.values == (0.1, 0.2, 0.3) + assert module.last_action == action + finally: + module.close() + + +def test_trigger_action_chunk_inference_requires_live_observation() -> None: + module = RobotPolicyModule(backend=FakeBackend(), contract=FakeContract()) + + try: + status = module.trigger_action_chunk_inference() + + assert not status.accepted + assert status.status == "not_ready" + assert status.message is not None + finally: + module.close() + + +def test_trigger_action_chunk_inference_returns_before_chunk_publication() -> None: + release_infer = threading.Event() + backend = FakeBackend(block_infer=release_infer) + module = RobotPolicyModule(backend=backend, contract=FakeContract()) + published_event = threading.Event() + published: list[RobotPolicyActionChunk] = [] + + def on_chunk(chunk: RobotPolicyActionChunk) -> None: + published.append(chunk) + published_event.set() + + module.policy_action_chunk.subscribe(on_chunk) + + try: + module.update_observation(RobotPolicyObservation(observations={"id": "obs-1"})) + + status = module.trigger_action_chunk_inference() + duplicate = module.trigger_action_chunk_inference() + + assert status.accepted + assert status.status == "started" + assert not duplicate.accepted + assert duplicate.status == "already_in_flight" + assert published == [] + + release_infer.set() + assert published_event.wait(timeout=2.0) + assert published[0].sequence == status.sequence + assert module.last_action_chunk == published[0] + finally: + release_infer.set() + module.close() + + +def test_trigger_action_chunk_inference_allows_next_request_after_completion() -> None: + backend = FakeBackend() + module = RobotPolicyModule(backend=backend, contract=FakeContract()) + published_event = threading.Event() + module.policy_action_chunk.subscribe(lambda _chunk: published_event.set()) + + try: + module.update_observation(RobotPolicyObservation(observations={"id": "obs-1"})) + first = module.trigger_action_chunk_inference() + assert first.accepted + assert published_event.wait(timeout=2.0) + + second = module.trigger_action_chunk_inference() + + assert second.accepted + assert second.sequence == 1 + finally: + module.close() diff --git a/dimos/robot_learning/policy_rollout/test_vla_jepa_libero_contract.py b/dimos/robot_learning/policy_rollout/test_vla_jepa_libero_contract.py new file mode 100644 index 0000000000..6d96a1cc63 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/test_vla_jepa_libero_contract.py @@ -0,0 +1,162 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from numpy.typing import NDArray +import pytest + +from dimos.robot_learning.policy_rollout.models import BackendOutputEnvelope, RobotPolicyObservation +from dimos.robot_learning.policy_rollout.vla_jepa_libero_contract import ( + VLA_JEPA_LIBERO_ACTION_SPACE_ID, + VlaJepaLiberoRobotContract, +) + + +def test_contract_converts_runtime_sample_to_backend_batch() -> None: + contract = VlaJepaLiberoRobotContract() + sample = _sample() + + batch = contract.to_backend_batch(sample) + + agentview = batch.payload["observation.images.image"] + wrist = batch.payload["observation.images.image2"] + state = batch.payload["observation.state"] + assert isinstance(agentview, np.ndarray) + assert isinstance(wrist, np.ndarray) + assert isinstance(state, np.ndarray) + agentview_array: NDArray[np.float32] = agentview + wrist_array: NDArray[np.float32] = wrist + assert agentview_array.shape == (3, 128, 128) + assert agentview_array.dtype == np.float32 + assert np.max(agentview_array) <= 1.0 + assert wrist_array.shape == (3, 128, 128) + assert state.shape == (8,) + assert batch.payload["task"] == "pick up the object" + assert batch.metadata["agentview_stream"] == "agentview" + assert batch.metadata["wrist_stream"] == "eye_in_hand" + + +def test_contract_flips_images_height_and_width_before_chw() -> None: + image = np.array( + [ + [[10, 0, 0], [20, 0, 0], [30, 0, 0]], + [[40, 0, 0], [50, 0, 0], [60, 0, 0]], + ], + dtype=np.uint8, + ) + contract = VlaJepaLiberoRobotContract() + + batch = contract.to_backend_batch(_sample(agentview=image)) + + agentview = batch.payload["observation.images.image"] + + assert isinstance(agentview, np.ndarray) + assert agentview.shape == (3, 2, 3) + assert np.allclose( + agentview[0], + np.array( + [[60 / 255.0, 50 / 255.0, 40 / 255.0], [30 / 255.0, 20 / 255.0, 10 / 255.0]], + dtype=np.float32, + ), + ) + + +def test_contract_rejects_missing_or_wrong_inputs() -> None: + contract = VlaJepaLiberoRobotContract() + with pytest.raises(ValueError, match="wrist"): + contract.to_backend_batch(_sample(streams=("agentview", "robot_state"))) + with pytest.raises(ValueError, match="dtype uint8"): + contract.to_backend_batch(_sample(agentview=np.zeros((128, 128, 3), dtype=np.float32))) + with pytest.raises(ValueError, match=r"shape \(8,\)"): + contract.to_backend_batch(_sample(state=[0.0] * 7)) + with pytest.raises(ValueError, match="task language"): + contract.to_backend_batch(_sample(language="")) + + +def test_contract_converts_backend_output_to_runtime_action() -> None: + contract = VlaJepaLiberoRobotContract() + + action = contract.from_backend_output( + BackendOutputEnvelope(output=(0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0)) + ) + + assert action.space_id == VLA_JEPA_LIBERO_ACTION_SPACE_ID + assert action.values == pytest.approx((0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0)) + + +def test_contract_rejects_invalid_backend_output() -> None: + contract = VlaJepaLiberoRobotContract() + with pytest.raises(ValueError, match="shape"): + contract.from_backend_output(BackendOutputEnvelope(output=(0.0,) * 6)) + with pytest.raises(ValueError, match="non-finite"): + contract.from_backend_output(BackendOutputEnvelope(output=(0.0,) * 6 + (float("nan"),))) + with pytest.raises(ValueError, match="within"): + contract.from_backend_output(BackendOutputEnvelope(output=(0.0,) * 6 + (2.0,))) + + +def test_contract_converts_backend_output_to_runtime_action_chunk() -> None: + contract = VlaJepaLiberoRobotContract() + + chunk = contract.chunk_from_backend_output( + BackendOutputEnvelope( + output=( + (0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0), + (0.1, 0.2, -0.2, 0.3, -0.3, 0.4, -1.0), + ) + ) + ) + + assert chunk.space_id == VLA_JEPA_LIBERO_ACTION_SPACE_ID + assert chunk.shape == (2, 7) + assert chunk.values[0] == pytest.approx((0.0, 0.1, -0.1, 0.2, -0.2, 0.3, 1.0)) + + +def test_contract_rejects_invalid_backend_action_chunk_output() -> None: + contract = VlaJepaLiberoRobotContract() + with pytest.raises(ValueError, match=r"shape \(N, 7\)"): + contract.chunk_from_backend_output(BackendOutputEnvelope(output=(0.0,) * 7)) + with pytest.raises(ValueError, match="empty"): + contract.chunk_from_backend_output(BackendOutputEnvelope(output=())) + with pytest.raises(ValueError, match="non-finite"): + contract.chunk_from_backend_output( + BackendOutputEnvelope(output=((0.0,) * 6 + (float("nan"),),)) + ) + with pytest.raises(ValueError, match="within"): + contract.chunk_from_backend_output(BackendOutputEnvelope(output=((0.0,) * 6 + (2.0,),))) + + +def _sample( + *, + streams: tuple[str, ...] = ("agentview", "eye_in_hand", "robot_state"), + agentview: np.ndarray | None = None, + wrist: np.ndarray | None = None, + state: list[float] | None = None, + language: str = "pick up the object", +) -> RobotPolicyObservation: + agentview_payload = ( + agentview if agentview is not None else np.zeros((128, 128, 3), dtype=np.uint8) + ) + wrist_payload = wrist if wrist is not None else np.ones((128, 128, 3), dtype=np.uint8) + state_values = state if state is not None else [0.0] * 8 + observations: dict[str, object] = {} + if "agentview" in streams: + observations["agentview"] = agentview_payload + if "eye_in_hand" in streams: + observations["eye_in_hand"] = wrist_payload + if "robot_state" in streams: + observations["robot_state"] = state_values + return RobotPolicyObservation( + observations=observations, + metadata={"language": language}, + ) diff --git a/dimos/robot_learning/policy_rollout/test_xvla_libero_contract.py b/dimos/robot_learning/policy_rollout/test_xvla_libero_contract.py new file mode 100644 index 0000000000..3db1e429c0 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/test_xvla_libero_contract.py @@ -0,0 +1,146 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import pytest + +from dimos.robot_learning.policy_rollout.models import BackendOutputEnvelope, RobotPolicyObservation +from dimos.robot_learning.policy_rollout.xvla_libero_contract import ( + XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID, + XvlaLiberoRobotContract, +) + + +def test_xvla_contract_converts_sample_to_libero_processor_batch() -> None: + contract = XvlaLiberoRobotContract() + + batch = contract.to_backend_batch(_sample()) + + agentview = batch.payload["observation.images.image"] + wrist = batch.payload["observation.images.image2"] + state = batch.payload["observation.state"] + assert isinstance(agentview, np.ndarray) + assert isinstance(wrist, np.ndarray) + assert isinstance(state, np.ndarray) + assert agentview.shape == (1, 3, 128, 128) + assert wrist.shape == (1, 3, 128, 128) + assert state.shape == (20,) + assert batch.payload["task"] == "pick up the object" + np.testing.assert_array_equal(batch.payload["domain_id"], [3]) + assert batch.metadata["policy_family"] == "xvla" + assert batch.metadata["action_mode"] == "absolute" + + +def test_xvla_contract_accepts_structured_robot_state() -> None: + contract = XvlaLiberoRobotContract() + sample = _sample( + state={ + "eef.pos": np.asarray([[0.1, 0.2, 0.3]], dtype=np.float32), + "eef.mat": np.eye(3, dtype=np.float32).reshape(1, 3, 3), + } + ) + + batch = contract.to_backend_batch(sample) + + state = batch.payload["observation.state"] + assert isinstance(state, np.ndarray) + np.testing.assert_allclose(state[:3], [0.1, 0.2, 0.3]) + + +def test_xvla_contract_rejects_missing_or_wrong_inputs() -> None: + contract = XvlaLiberoRobotContract() + with pytest.raises(ValueError, match="wrist"): + contract.to_backend_batch(_sample(streams=("agentview", "robot_state"))) + with pytest.raises(ValueError, match="dtype uint8"): + contract.to_backend_batch(_sample(agentview=np.zeros((128, 128, 3), dtype=np.float32))) + with pytest.raises(ValueError, match=r"shape \(8,\)"): + contract.to_backend_batch(_sample(state=[0.0] * 7)) + with pytest.raises(ValueError, match="quaternion"): + contract.to_backend_batch(_sample(state=[0.0] * 8)) + with pytest.raises(ValueError, match="task language"): + contract.to_backend_batch(_sample(language="")) + + +def test_xvla_contract_converts_finite_chunk_output_to_robot_policy_chunk() -> None: + contract = XvlaLiberoRobotContract() + + chunk = contract.chunk_from_backend_output( + BackendOutputEnvelope( + output=( + (0.0, 0.1, -0.1, 1.2, -2.2, 0.3, 1.0), + (0.1, 0.2, -0.2, 0.3, -0.3, 2.4, -1.0), + ), + metadata={"policy_family": "xvla"}, + ) + ) + + assert chunk.space_id == XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID + assert chunk.shape == (2, 7) + assert chunk.metadata["backend_metadata"] == {"policy_family": "xvla"} + assert chunk.values[0] == pytest.approx((0.0, 0.1, -0.1, 1.2, -2.2, 0.3, 1.0)) + + +def test_xvla_contract_converts_internal_padded_action_chunk_to_libero_7d() -> None: + contract = XvlaLiberoRobotContract() + + chunk = contract.chunk_from_backend_output( + BackendOutputEnvelope( + output=((0.1, 0.2, 0.3, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.6, *([0.0] * 10)),), + metadata={"policy_family": "xvla", "output_shape": [1, 20]}, + ) + ) + + assert chunk.space_id == XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID + assert chunk.shape == (1, 7) + assert chunk.values[0] == pytest.approx((0.1, 0.2, 0.3, 0.0, 0.0, 0.0, 1.0)) + + +def test_xvla_contract_rejects_incompatible_output_before_execution() -> None: + contract = XvlaLiberoRobotContract() + with pytest.raises(ValueError, match=r"shape \(N, 7\)"): + contract.chunk_from_backend_output(BackendOutputEnvelope(output=(0.0,) * 7)) + with pytest.raises(ValueError, match="empty"): + contract.chunk_from_backend_output(BackendOutputEnvelope(output=())) + with pytest.raises(ValueError, match="non-finite"): + contract.chunk_from_backend_output( + BackendOutputEnvelope(output=((0.0,) * 6 + (float("nan"),),)) + ) + with pytest.raises(ValueError, match="shape"): + contract.from_backend_output(BackendOutputEnvelope(output=(0.0,) * 6)) + + +def _sample( + *, + streams: tuple[str, ...] = ("agentview", "eye_in_hand", "robot_state"), + agentview: np.ndarray | None = None, + wrist: np.ndarray | None = None, + state: object | None = None, + language: str = "pick up the object", +) -> RobotPolicyObservation: + agentview_payload = ( + agentview if agentview is not None else np.zeros((128, 128, 3), dtype=np.uint8) + ) + wrist_payload = wrist if wrist is not None else np.ones((128, 128, 3), dtype=np.uint8) + state_values = state if state is not None else [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0] + observations: dict[str, object] = {} + if "agentview" in streams: + observations["agentview"] = agentview_payload + if "eye_in_hand" in streams: + observations["eye_in_hand"] = wrist_payload + if "robot_state" in streams: + observations["robot_state"] = state_values + return RobotPolicyObservation( + observations=observations, + metadata={"language": language}, + ) diff --git a/dimos/robot_learning/policy_rollout/vla_jepa_libero_contract.py b/dimos/robot_learning/policy_rollout/vla_jepa_libero_contract.py new file mode 100644 index 0000000000..cc93b13667 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/vla_jepa_libero_contract.py @@ -0,0 +1,147 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import Any, cast + +import numpy as np + +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + RobotPolicyAction, + RobotPolicyActionChunk, + RobotPolicyObservation, +) + +VLA_JEPA_LIBERO_ACTION_SPACE_ID = "libero.ee_delta_6d_gripper.normalized.v1" + + +class VlaJepaLiberoRobotContract: + """Hardcoded VLA-JEPA LIBERO policy IO contract for the v1 rollout gate.""" + + def __init__( + self, + *, + agentview_stream: str = "agentview", + wrist_stream_candidates: Sequence[str] = ("eye_in_hand", "wrist", "robot0_eye_in_hand"), + state_stream: str = "robot_state", + ) -> None: + self._agentview_stream = agentview_stream + self._wrist_stream_candidates = tuple(wrist_stream_candidates) + self._state_stream = state_stream + + def to_backend_batch(self, sample: RobotPolicyObservation) -> BackendBatch: + observations = sample.observations + agentview = self._image_payload(observations, self._agentview_stream) + wrist_stream = self._select_wrist_stream(observations) + wrist = self._image_payload(observations, wrist_stream) + state = self._state_vector(observations) + language = self._language(sample) + + return BackendBatch( + payload={ + "observation.images.image": agentview, + "observation.images.image2": wrist, + "observation.state": state, + "task": language, + }, + metadata={ + "agentview_stream": self._agentview_stream, + "wrist_stream": wrist_stream, + }, + ) + + def from_backend_output(self, output: BackendOutputEnvelope) -> RobotPolicyAction: + values = np.asarray(output.output, dtype=np.float32) + if values.shape != (7,): + raise ValueError(f"VLA-JEPA LIBERO action must have shape (7,), got {values.shape}") + if not np.all(np.isfinite(values)): + raise ValueError("VLA-JEPA LIBERO action contains non-finite values") + if np.any((values < -1.0) | (values > 1.0)): + raise ValueError("VLA-JEPA LIBERO action must be within [-1, 1]") + return RobotPolicyAction( + space_id=VLA_JEPA_LIBERO_ACTION_SPACE_ID, + values=tuple(float(value) for value in values), + metadata={"backend_metadata": dict(output.metadata)}, + ) + + def chunk_from_backend_output(self, output: BackendOutputEnvelope) -> RobotPolicyActionChunk: + if isinstance(output.output, Sequence) and len(output.output) == 0: + raise ValueError("VLA-JEPA LIBERO action chunk must not be empty") + values = np.asarray(output.output, dtype=np.float32) + if values.ndim != 2 or values.shape[1] != 7: + raise ValueError( + f"VLA-JEPA LIBERO action chunk must have shape (N, 7), got {values.shape}" + ) + if not np.all(np.isfinite(values)): + raise ValueError("VLA-JEPA LIBERO action chunk contains non-finite values") + if np.any((values < -1.0) | (values > 1.0)): + raise ValueError("VLA-JEPA LIBERO action chunk must be within [-1, 1]") + return RobotPolicyActionChunk( + space_id=VLA_JEPA_LIBERO_ACTION_SPACE_ID, + values=tuple(tuple(float(item) for item in row) for row in values), + metadata={"backend_metadata": dict(output.metadata)}, + ) + + def _select_wrist_stream(self, observations: Mapping[str, object]) -> str: + for stream in self._wrist_stream_candidates: + if stream in observations: + return stream + raise ValueError( + "missing VLA-JEPA LIBERO wrist/eye-in-hand image stream; " + f"expected one of {self._wrist_stream_candidates}" + ) + + def _image_payload( + self, + observations: Mapping[str, object], + stream: str, + ) -> np.ndarray: + payload = observations.get(stream) + if payload is None: + raise ValueError(f"missing VLA-JEPA LIBERO image stream {stream!r}") + image = np.asarray(payload) + if image.ndim != 3 or image.shape[2] != 3: + raise ValueError(f"image stream {stream!r} must have HWC RGB shape") + if image.dtype != np.uint8: + raise ValueError(f"image stream {stream!r} must have dtype uint8") + flipped = np.flip(image, axis=(0, 1)).copy() + return np.transpose(flipped, (2, 0, 1)).astype(np.float32) / 255.0 + + def _state_vector(self, observations: Mapping[str, object]) -> np.ndarray: + state = observations.get(self._state_stream) + if state is None: + raise ValueError(f"missing VLA-JEPA LIBERO state stream {self._state_stream!r}") + array = np.asarray(state, dtype=np.float32) + if array.shape != (8,): + raise ValueError(f"VLA-JEPA LIBERO robot state must have shape (8,), got {array.shape}") + if not np.all(np.isfinite(array)): + raise ValueError("VLA-JEPA LIBERO robot state contains non-finite values") + return array + + def _language(self, sample: RobotPolicyObservation) -> str: + language = sample.metadata.get("language") + if isinstance(language, str) and language.strip(): + return language + observed_language = sample.observations.get("language") + if isinstance(observed_language, str) and observed_language.strip(): + return observed_language + raise ValueError("missing VLA-JEPA LIBERO task language") + + +def create_contract(**params: object) -> VlaJepaLiberoRobotContract: + return VlaJepaLiberoRobotContract(**cast("dict[str, Any]", params)) diff --git a/dimos/robot_learning/policy_rollout/xvla_libero_contract.py b/dimos/robot_learning/policy_rollout/xvla_libero_contract.py new file mode 100644 index 0000000000..459a446c12 --- /dev/null +++ b/dimos/robot_learning/policy_rollout/xvla_libero_contract.py @@ -0,0 +1,290 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import cast + +import numpy as np + +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + RobotPolicyAction, + RobotPolicyActionChunk, + RobotPolicyObservation, +) + +XVLA_LIBERO_DEFAULT_DOMAIN_ID = 3 +XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID = "libero.ee_pose_axis_angle_gripper.absolute.v1" + + +class XvlaLiberoRobotContract: + """X-VLA LIBERO IO contract for LeRobot's LIBERO processor pipeline.""" + + def __init__( + self, + *, + agentview_stream: str = "agentview", + wrist_stream_candidates: Sequence[str] = ("eye_in_hand", "wrist", "robot0_eye_in_hand"), + state_stream: str = "robot_state", + structured_state_stream: str = "xvla_robot_state", + domain_id: int = XVLA_LIBERO_DEFAULT_DOMAIN_ID, + ) -> None: + self._agentview_stream = agentview_stream + self._wrist_stream_candidates = tuple(wrist_stream_candidates) + self._state_stream = state_stream + self._structured_state_stream = structured_state_stream + self._domain_id = int(domain_id) + + def to_backend_batch(self, sample: RobotPolicyObservation) -> BackendBatch: + observations = sample.observations + agentview = self._image_payload(observations, self._agentview_stream, flip=True) + wrist_stream = self._select_wrist_stream(observations) + wrist = self._image_payload(observations, wrist_stream, flip=False) + state = self._state_payload(observations) + language = self._language(sample) + + return BackendBatch( + payload={ + "observation.images.image": agentview, + "observation.images.image2": wrist, + "observation.state": state, + "task": language, + "domain_id": np.asarray([self._domain_id], dtype=np.int64), + }, + metadata={ + "agentview_stream": self._agentview_stream, + "wrist_stream": wrist_stream, + "policy_family": "xvla", + "domain_id": self._domain_id, + "action_mode": "absolute", + }, + ) + + def from_backend_output(self, output: BackendOutputEnvelope) -> RobotPolicyAction: + raw_values = np.asarray(output.output, dtype=np.float32) + if raw_values.shape != (7,): + raw_values = _xvla_internal_action_to_libero(raw_values.reshape(1, -1))[0] + values = _validated_action_values(raw_values, shape=(7,), label="X-VLA LIBERO action") + return RobotPolicyAction( + space_id=XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID, + values=tuple(float(value) for value in values), + metadata={"backend_metadata": dict(output.metadata)}, + ) + + def chunk_from_backend_output(self, output: BackendOutputEnvelope) -> RobotPolicyActionChunk: + if isinstance(output.output, Sequence) and len(output.output) == 0: + raise ValueError("X-VLA LIBERO action chunk must not be empty") + values = np.asarray(output.output, dtype=np.float32) + if values.ndim != 2: + raise ValueError( + f"X-VLA LIBERO action chunk must have shape (N, 7), got {values.shape}" + ) + if values.shape[1] != 7: + values = _xvla_internal_action_to_libero(values) + _validate_finite(values, label="X-VLA LIBERO action chunk") + return RobotPolicyActionChunk( + space_id=XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID, + values=tuple(tuple(float(item) for item in row) for row in values), + metadata={"backend_metadata": dict(output.metadata)}, + ) + + def _select_wrist_stream(self, observations: Mapping[str, object]) -> str: + for stream in self._wrist_stream_candidates: + if stream in observations: + return stream + raise ValueError( + "missing X-VLA LIBERO wrist/eye-in-hand image stream; " + f"expected one of {self._wrist_stream_candidates}" + ) + + def _image_payload( + self, observations: Mapping[str, object], stream: str, *, flip: bool + ) -> np.ndarray: + payload = observations.get(stream) + if payload is None: + raise ValueError(f"missing X-VLA LIBERO image stream {stream!r}") + image = np.asarray(payload) + if image.ndim != 3 or image.shape[2] != 3: + raise ValueError(f"image stream {stream!r} must have HWC RGB shape") + if image.dtype != np.uint8: + raise ValueError(f"image stream {stream!r} must have dtype uint8") + if flip: + image = np.flip(image, axis=(0, 1)).copy() + chw = np.transpose(image, (2, 0, 1)).astype(np.float32) / 255.0 + return np.expand_dims(chw, axis=0) + + def _state_payload(self, observations: Mapping[str, object]) -> np.ndarray: + structured_payload = observations.get(self._structured_state_stream) + if isinstance(structured_payload, Mapping): + return _structured_robot_state_to_xvla_state( + _structured_robot_state(structured_payload) + ) + state_payload = observations.get(self._state_stream) + if state_payload is None: + raise ValueError(f"missing X-VLA LIBERO state stream {self._state_stream!r}") + if isinstance(state_payload, Mapping): + robot_state = _structured_robot_state(state_payload) + else: + robot_state = _state_vector_to_structured_robot_state(state_payload) + return _structured_robot_state_to_xvla_state(robot_state) + + def _language(self, sample: RobotPolicyObservation) -> str: + language = sample.metadata.get("language") + if isinstance(language, str) and language.strip(): + return language + observed_language = sample.observations.get("language") + if isinstance(observed_language, str) and observed_language.strip(): + return observed_language + raise ValueError("missing X-VLA LIBERO task language") + + +def _structured_robot_state(payload: Mapping[str, object]) -> dict[str, np.ndarray]: + pos = _batched_float_array(payload.get("eef.pos"), shape=(1, 3), label="eef.pos") + mat = _batched_float_array(payload.get("eef.mat"), shape=(1, 3, 3), label="eef.mat") + return {"eef.pos": pos, "eef.mat": mat} + + +def _state_vector_to_structured_robot_state(payload: object) -> dict[str, np.ndarray]: + vector = np.asarray(payload, dtype=np.float32) + if vector.shape != (8,): + raise ValueError(f"X-VLA LIBERO robot state must have shape (8,), got {vector.shape}") + if not np.all(np.isfinite(vector)): + raise ValueError("X-VLA LIBERO robot state contains non-finite values") + pos = vector[:3].reshape(1, 3) + quat = _normalized_quaternion(vector[3:7]) + mat = _quaternion_to_rotation_matrix(quat).reshape(1, 3, 3) + return {"eef.pos": pos.astype(np.float32), "eef.mat": mat.astype(np.float32)} + + +def _structured_robot_state_to_xvla_state(robot_state: Mapping[str, np.ndarray]) -> np.ndarray: + pos = robot_state["eef.pos"] + mat = robot_state["eef.mat"] + rot6d = np.concatenate((mat[:, :3, 0], mat[:, :3, 1]), axis=-1) + extra = np.zeros((pos.shape[0], 1), dtype=np.float32) + proprio = np.concatenate((pos, rot6d.astype(np.float32), extra), axis=-1) + state = np.concatenate((proprio, np.zeros_like(proprio)), axis=-1) + return state[0].astype(np.float32) + + +def _normalized_quaternion(value: np.ndarray) -> np.ndarray: + norm = float(np.linalg.norm(value)) + if not np.isfinite(norm) or norm <= 0.0: + raise ValueError("X-VLA LIBERO robot state quaternion must have non-zero finite norm") + return value.astype(np.float32) / norm + + +def _quaternion_to_rotation_matrix(quat: np.ndarray) -> np.ndarray: + x, y, z, w = (float(item) for item in quat) + return np.asarray( + [ + [1.0 - 2.0 * (y * y + z * z), 2.0 * (x * y - z * w), 2.0 * (x * z + y * w)], + [2.0 * (x * y + z * w), 1.0 - 2.0 * (x * x + z * z), 2.0 * (y * z - x * w)], + [2.0 * (x * z - y * w), 2.0 * (y * z + x * w), 1.0 - 2.0 * (x * x + y * y)], + ], + dtype=np.float32, + ) + + +def _batched_float_array(value: object, *, shape: tuple[int, ...], label: str) -> np.ndarray: + array = np.asarray(value, dtype=np.float32) + if array.shape != shape: + raise ValueError(f"X-VLA LIBERO {label} must have shape {shape}, got {array.shape}") + if not np.all(np.isfinite(array)): + raise ValueError(f"X-VLA LIBERO {label} contains non-finite values") + return array + + +def _validated_action_values(value: object, *, shape: tuple[int, ...], label: str) -> np.ndarray: + values = np.asarray(value, dtype=np.float32) + if values.shape != shape: + raise ValueError(f"{label} must have shape {shape}, got {values.shape}") + _validate_finite(values, label=label) + return values + + +def _xvla_internal_action_to_libero(values: np.ndarray) -> np.ndarray: + if values.ndim != 2 or values.shape[1] < 10: + raise ValueError(f"X-VLA LIBERO action chunk must have shape (N, 7), got {values.shape}") + _validate_finite(values, label="X-VLA LIBERO internal action chunk") + target_eef = values[:, :3] + rotation_6d = values[:, 3:9] + gripper = values[:, 9:10] + axis_angle = _rotation_6d_to_axis_angle(rotation_6d) + converted = np.concatenate((target_eef, axis_angle, gripper), axis=-1).astype(np.float32) + converted[:, -1] = np.where(converted[:, -1] > 0.5, 1.0, -1.0) + return converted + + +def _rotation_6d_to_axis_angle(rotation_6d: np.ndarray) -> np.ndarray: + first = rotation_6d[:, :3] + second = rotation_6d[:, 3:6] + basis_1 = _normalize_rows(first) + second_orthogonal = second - np.sum(basis_1 * second, axis=-1, keepdims=True) * basis_1 + basis_2 = _normalize_rows(second_orthogonal) + basis_3 = np.cross(basis_1, basis_2) + matrices = np.stack((basis_1, basis_2, basis_3), axis=-1) + return _rotation_matrix_to_axis_angle(matrices) + + +def _normalize_rows(values: np.ndarray) -> np.ndarray: + norms = np.linalg.norm(values, axis=-1, keepdims=True) + if np.any(~np.isfinite(norms)) or np.any(norms <= 1.0e-8): + raise ValueError("X-VLA LIBERO rotation 6D values must have non-zero finite rows") + return values / norms + + +def _rotation_matrix_to_axis_angle(matrices: np.ndarray) -> np.ndarray: + axis_angles: list[np.ndarray] = [] + for matrix in matrices: + trace = float(np.trace(matrix)) + cos_angle = np.clip((trace - 1.0) / 2.0, -1.0, 1.0) + angle = float(np.arccos(cos_angle)) + if angle < 1.0e-6: + axis_angles.append(np.zeros(3, dtype=np.float32)) + continue + denominator = 2.0 * np.sin(angle) + axis = ( + np.asarray( + [ + matrix[2, 1] - matrix[1, 2], + matrix[0, 2] - matrix[2, 0], + matrix[1, 0] - matrix[0, 1], + ], + dtype=np.float32, + ) + / denominator + ) + axis_angles.append((axis * angle).astype(np.float32)) + return np.stack(axis_angles, axis=0) + + +def _validate_finite(values: np.ndarray, *, label: str) -> None: + if not np.all(np.isfinite(values)): + raise ValueError(f"{label} contains non-finite values") + + +def create_contract(**params: object) -> XvlaLiberoRobotContract: + wrist_stream_candidates = params.get( + "wrist_stream_candidates", ("eye_in_hand", "wrist", "robot0_eye_in_hand") + ) + return XvlaLiberoRobotContract( + agentview_stream=str(params.get("agentview_stream", "agentview")), + wrist_stream_candidates=cast("Sequence[str]", wrist_stream_candidates), + state_stream=str(params.get("state_stream", "robot_state")), + structured_state_stream=str(params.get("structured_state_stream", "xvla_robot_state")), + domain_id=int(str(params.get("domain_id", XVLA_LIBERO_DEFAULT_DOMAIN_ID))), + ) diff --git a/dimos/simulation/runtime_client/shm_motor.py b/dimos/simulation/runtime_client/shm_motor.py index ae7e0a757f..04d99cdb05 100644 --- a/dimos/simulation/runtime_client/shm_motor.py +++ b/dimos/simulation/runtime_client/shm_motor.py @@ -65,20 +65,37 @@ def write(self, payload: JsonPayload) -> None: f"payload too large for SHM block {self.name}: {len(data)}>{max_payload}" ) buf = cast("memoryview", self._shm.buf) - buf[: _HEADER.size] = _HEADER.pack(sequence, len(data)) + buf[: _HEADER.size] = _HEADER.pack(sequence, 0) buf[_HEADER.size : _HEADER.size + len(data)] = data + buf[: _HEADER.size] = _HEADER.pack(sequence, len(data)) def read(self) -> JsonPayload: buf = cast("memoryview", self._shm.buf) - sequence, length = _HEADER.unpack(bytes(buf[: _HEADER.size])) - if length == 0: - return {"sequence": sequence} - data = bytes(buf[_HEADER.size : _HEADER.size + length]) - value = json.loads(data.decode("utf-8")) - if not isinstance(value, dict): - raise ValueError(f"SHM block {self.name} did not contain a JSON object") - value["sequence"] = sequence - return value + last_error: json.JSONDecodeError | UnicodeDecodeError | None = None + for _ in range(10): + sequence, length = _HEADER.unpack(bytes(buf[: _HEADER.size])) + if length == 0: + time.sleep(0.0001) + continue + data = bytes(buf[_HEADER.size : _HEADER.size + length]) + confirm_sequence, confirm_length = _HEADER.unpack(bytes(buf[: _HEADER.size])) + if (confirm_sequence, confirm_length) != (sequence, length): + time.sleep(0.0001) + continue + try: + value = json.loads(data.decode("utf-8")) + except (json.JSONDecodeError, UnicodeDecodeError) as exc: + last_error = exc + time.sleep(0.0001) + continue + if not isinstance(value, dict): + raise ValueError(f"SHM block {self.name} did not contain a JSON object") + value["sequence"] = sequence + return value + if last_error is not None: + raise last_error + sequence, _ = _HEADER.unpack(bytes(buf[: _HEADER.size])) + return {"sequence": sequence} def close(self) -> None: self._shm.close() diff --git a/docs/development/lerobot_libero_policy_rollout.md b/docs/development/lerobot_libero_policy_rollout.md new file mode 100644 index 0000000000..206c679d98 --- /dev/null +++ b/docs/development/lerobot_libero_policy_rollout.md @@ -0,0 +1,281 @@ +# LeRobot LIBERO policy rollout gate + +This optional/manual gate evaluates LeRobot LIBERO checkpoints through DimOS +robot-learning seams. The default policy is the official VLA-JEPA LIBERO +checkpoint. The script also has an X-VLA LIBERO configuration for a two-stage +policy-agnostic gate. There are two runnable paths: + +- **Fast benchmark path** (default): lockstep reset/snapshot/inference/action/step + ownership stays in the benchmark runner so simulation can remain simple, + parallelizable, and faster-than-realtime where possible. +- **Live policy stream path** (`--live-policy-stream`): runtime observation + assembly, `RobotPolicyModule`, and `ControlCoordinator` run as DimOS modules. + The policy module emits action chunks on a stream, the coordinator executes + them via the policy chunk control task, and the demo forwards the resulting + native LIBERO action row to the runtime. This is the realtime parity path. + +Default fast benchmark flow: + +```text +BenchmarkPolicyEvalRunner + -> RuntimeSidecarClient.reset(...) + -> LiberoRobotPolicyObservationBuilder.build(...) + -> RobotPolicyModule.infer_action(RobotPolicyObservation) + -> VlaJepaLiberoRobotContract.to_backend_batch(...) + -> LeRobotBackend.infer_batch(...) + -> VlaJepaLiberoRobotContract.from_backend_output(...) + -> RobotPolicyAction + -> BenchmarkPolicyEvalRunner adapts to RuntimeActionFrame + -> RuntimeSidecarClient.step(...) +``` + +`RobotPolicyModule` is a first-class DimOS `Module`. It owns backend lifecycle, +policy reset, contract conversion, inference, and `RobotPolicyAction` emission. +Benchmark evaluation owns sidecar reset/step, sample construction, action-frame +adaptation, scoring, artifacts, videos, and the gate. `BenchmarkPolicyEvalModule` +wraps the runner for module/blueprint-compatible lockstep evaluation. + +Policy backends and contracts are selected through lazy registries: + +```text +RobotPolicyModule.blueprint( + backend_type="lerobot", + backend_params={"policy_family": "vla_jepa", "checkpoint_id": "lerobot/VLA-JEPA-LIBERO"}, + contract_type="vla_jepa_libero", +) +``` + +Supported policy-family defaults: + +| `--policy-family` | Default checkpoint | Default contract | LeRobot extra | +| --- | --- | --- | --- | +| `vla_jepa` | `lerobot/VLA-JEPA-LIBERO` | `vla_jepa_libero` | `vla_jepa` | +| `xvla` | `lerobot/xvla-libero` | `xvla_libero` | `xvla` | + +Override `--checkpoint` or `--contract-type` only when testing a known-compatible +variant. A checkpoint swap alone is not enough for policy-family changes because +LeRobot policy families can use different processor inputs, action modes, and +optional dependency extras. + +The helper `lerobot_libero_policy_eval_blueprint(...)` returns a blueprint-shaped +composition of `RobotPolicyModule` and `BenchmarkPolicyEvalModule` for this gate. + +The default fast benchmark path intentionally bypasses `ControlCoordinator`. +The sidecar still runs in native LIBERO action mode and accepts the official +relative end-effector delta + gripper action surface: + +```text +space_id = libero.ee_delta_6d_gripper.normalized.v1 +shape = (7,) +bounds = [-1, 1] +``` + +The live policy stream path preserves the same native LIBERO action semantics. +It does **not** reinterpret VLA-JEPA actions as Panda joint targets. Instead, +the policy chunk task claims a synthetic 7-DOF action surface, emits one +index-bounded normalized action row through `ControlCoordinator`, and the demo +uses that row as a `RuntimeActionFrame` with the same +`libero.ee_delta_6d_gripper.normalized.v1` space id. + +Live stream flow: + +```text +LiberoRobotPolicyObservationBuilder.build(...) + -> RobotPolicyModule.update_observation(RobotPolicyObservation) + -> RobotPolicyModule.trigger_action_chunk_inference() + -> LeRobotBackend.infer_batch(..., use_action_chunk=True) + -> VlaJepaLiberoRobotContract.chunk_from_backend_output(...) + -> RobotPolicyModule.policy_action_chunk stream + -> ControlCoordinator.robot_policy_action_chunk stream input + -> PolicyChunkControlTask index-bounded execution + -> RuntimeActionFrame(space_id=libero.ee_delta_6d_gripper.normalized.v1) + -> LiberoProRuntimeModule.step(...) +``` + +## Dependencies + +Run this only in an environment with: + +- The standard LIBERO Python package installed. The runner auto-discovers the + package `bddl_files` and `init_files` roots. You can still pass explicit + `--bddl-root` and `--init-states-root` paths for custom or offline assets. +- LeRobot from GitHub main. The PyPI `lerobot` release may not include the + newest policy families yet. +- The selected checkpoint accessible from Hugging Face, unless using the + fake-backend smoke path. + +On current Python/CMake environments, LIBERO's `egl-probe` dependency may need +the CMake compatibility policy set during install/run: + +```bash +export CMAKE_POLICY_VERSION_MINIMUM=3.5 +``` + +## Full 50-episode gate + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +uv run \ + --with libero \ + --with "lerobot[vla_jepa] @ git+https://github.com/huggingface/lerobot.git" \ + python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --artifact-dir artifacts/benchmark/lerobot-vla-jepa-libero +``` + +The episode matrix is generated by the runner: `libero_object` task indices +`0..9` crossed with init state indices `[0, 1, 2, 3, 4]`, for 50 total episodes. +The run passes only when: + +```text +success_rate > 0.50 +``` + +Use `--device cuda` or another LeRobot-supported device when needed. + +## X-VLA two-stage gate + +X-VLA support is selected with `--policy-family xvla`. The backend resolves +`lerobot.policies.xvla.modeling_xvla.XVLAPolicy`, defaults to +`lerobot/xvla-libero`, and writes `policy_family`, `contract_type`, `gate_stage`, +and checkpoint metadata into artifacts. + +Install X-VLA outside the main project dependencies. In the LIBERO sidecar or +another disposable environment, PyPI `lerobot[xvla]` is sufficient for the +current smoke/gate path: + +```bash +uv pip install 'lerobot[xvla]' +PYTEST_VERSION=1 uv run python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py --help +``` + +If validating against LeRobot source instead, use an isolated `uv run --with` +invocation or sidecar virtualenv and verify dependency resolution before running +the gate. Do not add X-VLA dependencies to the main DimOS `pyproject.toml`. + +Stage 1 validates the synchronous benchmark path without ControlCoordinator +policy-chunk execution: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +PYTEST_VERSION=1 uv run python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --policy-family xvla \ + --device cuda \ + --episodes-limit 10 \ + --gate-stage benchmark \ + --artifact-dir artifacts/benchmark/lerobot-xvla-libero-benchmark-10 +``` + +Stage 2 runs the module-native live policy stream path after Stage 1 passes: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +PYTEST_VERSION=1 uv run python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --policy-family xvla \ + --live-policy-stream \ + --device cuda \ + --episodes-limit 10 \ + --save-videos \ + --gate-stage live \ + --artifact-dir artifacts/benchmark/lerobot-xvla-libero-live-10-videos +``` + +Both X-VLA stages use the same hard threshold: + +```text +success_rate > 0.50 +``` + +LeRobot's X-VLA docs identify the LIBERO checkpoint with `control_mode=absolute` +and `domain_id=3`. The DimOS `xvla_libero` contract prepares a 20D X-VLA state, +passes `domain_id=3`, flips the main camera by 180° to match LeRobot's LIBERO +processor convention, and emits absolute end-effector pose actions with: + +```text +space_id = libero.ee_pose_axis_angle_gripper.absolute.v1 +shape = (7,) +bounds = finite values; axis-angle entries are not normalized to [-1, 1] +``` + +The LIBERO runtime uses `native_absolute` for X-VLA so OSC pose control keeps +`use_delta=False`. Do not clamp or reinterpret X-VLA actions as +`libero.ee_delta_6d_gripper.normalized.v1`; that action space is reserved for +VLA-JEPA's normalized delta path. If the real gate fails during setup or +contract smoke, check whether the installed LeRobot checkpoint processor or the +local LIBERO runtime action mode changed before treating the failure as a +policy-quality regression. + +Latest local Stage 1 artifact: + +```text +artifacts/benchmark/lerobot-xvla-libero-benchmark-10-image-flip-videos/summary.json +episodes=10 successes=9 success_rate=0.9 passed=true +``` + +## Live policy stream parity gate + +Run the realtime-style path by adding `--live-policy-stream`. Acceptance uses +the real `lerobot/VLA-JEPA-LIBERO` checkpoint over a 10-episode `libero_object` +slice and the same hard threshold: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +uv run \ + --with libero \ + --with "lerobot[vla_jepa] @ git+https://github.com/huggingface/lerobot.git" \ + python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --live-policy-stream \ + --device cuda \ + --episodes-limit 10 \ + --save-videos \ + --artifact-dir artifacts/benchmark/lerobot-vla-jepa-libero-live-10-videos +``` + +`--fake-backend` is only a plumbing smoke test. Do not use it for live parity +acceptance. `--live-chunk-timeout-s` caps how long each tick waits for a +coordinator-emitted action, and `--live-max-stale-waits` aborts the episode +after repeated live action timeouts so inference-shape or backend failures fail +fast instead of spending `max_steps * timeout` seconds on one episode. + +## Native-action smoke path + +To test protocol/sidecar/native-action plumbing without downloading LeRobot: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +uv run --with libero \ + python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --fake-backend \ + --live-policy-stream \ + --episodes-limit 1 \ + --no-enforce-gate +``` + +This still constructs the module-backed workflow and uses the selected real +contract (`vla_jepa_libero` by default, `xvla_libero` with +`--policy-family xvla`), but the backend returns a fixed 7D action or a +single-row action chunk when `--live-policy-stream` is set. + +## Artifacts + +The top-level artifact directory contains: + +- `summary.json` +- `episodes.jsonl` +- `runtime_description.json` +- `checkpoint_metadata.json` +- `run_config.json` +- `cleanup_status.json` + +Each per-episode subdirectory contains the runner artifacts for that episode and +the sidecar log. Full videos or image dumps are not saved by default. Passing +`--save-videos` writes lightweight MP4 previews for the configured camera streams +under each episode directory, for example: + +```text +episodes/libero_object_task0_init0/videos/libero_object_task0_init0/agentview.mp4 +``` + +Live stream runs also write `live_path_diagnostics.json` per episode. It records +chunk counts, accepted refill triggers, consumed actions, inference status +counts, stale waits/deactivations, and the last command sequence observed from +`ControlCoordinator`. diff --git a/docs/development/lerobot_libero_policy_rollout_handoff.md b/docs/development/lerobot_libero_policy_rollout_handoff.md new file mode 100644 index 0000000000..219356661e --- /dev/null +++ b/docs/development/lerobot_libero_policy_rollout_handoff.md @@ -0,0 +1,337 @@ +# LeRobot LIBERO policy rollout gate handoff + +This handoff captures the verified real 50-episode +`lerobot/VLA-JEPA-LIBERO` benchmark gate and the command needed to reproduce it. + +## Goal + +Run the real policy benchmark and verify: + +```text +checkpoint = lerobot/VLA-JEPA-LIBERO +suite = libero_object +episodes = 50 # task_index 0..9 × init_state_index 0..4 +pass condition = success_rate > 0.50 +``` + +The gate is implemented in: + +```text +scripts/benchmarks/demo_lerobot_libero_policy_rollout.py +``` + +Expected artifact directory: + +```text +artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full +``` + +## Verified local result + +The real non-fake 50-episode gate passed locally with checkpoint +`lerobot/VLA-JEPA-LIBERO` on CUDA: + +```text +episodes=50 successes=45 success_rate=0.900 passed=True +``` + +Verified summary artifact: + +```json +{ + "episodes": 50, + "passed": true, + "success_rate": 0.9, + "success_threshold": 0.5, + "successes": 45 +} +``` + +Artifact directory: + +```text +artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full +``` + +## Current repo state + +Implementation is complete for the OpenSpec change +`add-lerobot-libero-policy-rollout`: + +- runtime protocol `RuntimeActionFrame` union +- native LIBERO action mode in sidecar +- `RobotPolicyModule` +- `LeRobotBackend` +- `VlaJepaLiberoRobotContract` +- `BenchmarkPolicyEvalRunner` +- 50-episode gate script +- optional MP4 video artifacts +- standard LIBERO package asset auto-discovery +- noninteractive LIBERO config creation +- checkpoint-saved LeRobot processor loading for VLA-JEPA normalization stats +- LIBERO image orientation parity with LeRobot's `LiberoProcessorStep` +- native reset parity: 10 no-op settle steps, relative `OSC_POSE` control, and + an effective simulator horizon that preserves the requested policy horizon + +Validation already run locally: + +```text +real 50-episode gate: 45/50 successes, success_rate=0.900, passed=True +5-episode smoke: 5/5 successes, success_rate=1.000, passed=True +targeted pytest: 25 passed +ruff: passed +pytest targeted: 44 passed earlier; 16 passed after LIBERO setup/doc updates +openspec validate add-lerobot-libero-policy-rollout --type change: passed earlier +``` + +## Important environment findings + +### 1. Use LeRobot from GitHub main + +PyPI `lerobot==0.5.1` is not sufficient for this gate in the current Python 3.12 +environment. It did not provide a clean VLA-JEPA import path. Installing from +GitHub main produced `lerobot==0.5.2` and exposed: + +```text +lerobot.policies.vla_jepa.modeling_vla_jepa +``` + +Use a GitHub install, ideally with the VLA-JEPA extra. + +### 2. LIBERO may need a CMake compatibility variable + +Installing `libero` can fail while building `egl-probe==1.0.2` under newer +CMake. This worked locally: + +```bash +export CMAKE_POLICY_VERSION_MINIMUM=3.5 +``` + +### 3. Standard LIBERO assets are package assets + +The official standard LIBERO benchmark assets are part of the installed LIBERO +package: + +```text +libero/libero/bddl_files +libero/libero/init_files +``` + +The runner/sidecar now auto-discovers those roots. Do **not** use +`zhouxueyang/LIBERO-Pro` for this gate: that dataset contains perturbation-style +folders such as `libero_object_task`, not the standard `libero_object` suite used +by `lerobot/VLA-JEPA-LIBERO`. + +### 4. First startup may download LIBERO robot assets + +The first LIBERO sidecar startup may download robot/assets from Hugging Face. +Use a longer startup timeout. + +## Recommended remote command + +From the repo root: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +MUJOCO_GL=egl \ +uv run \ + --with libero \ + --with 'lerobot[vla_jepa] @ git+https://github.com/huggingface/lerobot.git' \ + python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --device cuda \ + --save-videos \ + --startup-timeout-s 240 \ + --artifact-dir artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full +``` + +If the direct-reference extra syntax fails in `uv`, fall back to explicit deps: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +MUJOCO_GL=egl \ +uv run \ + --with libero \ + --with git+https://github.com/huggingface/lerobot.git \ + --with diffusers \ + python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --device cuda \ + --save-videos \ + --startup-timeout-s 240 \ + --artifact-dir artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full +``` + +If more missing VLA-JEPA dependencies appear, prefer the first command form with +`lerobot[vla_jepa]` from GitHub main rather than adding packages one by one. + +## Local attempts already made + +### Final real 50-episode gate + +Command: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +MUJOCO_GL=egl \ +uv run \ + --with libero \ + --with 'lerobot[vla_jepa] @ git+https://github.com/huggingface/lerobot.git' \ + python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --device cuda \ + --save-videos \ + --startup-timeout-s 240 \ + --artifact-dir artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full +``` + +Result: + +```text +episodes=50 successes=45 success_rate=0.900 passed=True +``` + +Key fixes needed to reach this result: + +- `LeRobotBackend` loads checkpoint-saved policy pre/postprocessors so VLA-JEPA + state normalization and action unnormalization use checkpoint stats. +- `VlaJepaLiberoRobotContract` flips LIBERO images on height and width axes and + emits contiguous CHW float32 `[0, 1]` tensors. +- The sidecar native reset mirrors LeRobot's 10 no-op settle steps and relative + `OSC_POSE` control; the simulator horizon is extended by those settle steps so + the benchmark still gets the configured policy horizon. + +### Setup-blocked attempt before fixes + +Command: + +```bash +uv run python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --allow-asset-bootstrap \ + --save-videos \ + --artifact-dir artifacts/benchmark/lerobot-vla-jepa-libero-real-gate +``` + +Result: setup artifact created, but policy did not execute. + +Root cause: + +```text +LIBERO_PRO_HF_REPO_ID was not set +``` + +This is no longer the recommended path for the standard LIBERO gate. + +### Fake-backend LIBERO package preflight after fixes + +Command: + +```bash +CMAKE_POLICY_VERSION_MINIMUM=3.5 \ +uv run --with libero \ + python scripts/benchmarks/demo_lerobot_libero_policy_rollout.py \ + --fake-backend \ + --episodes-limit 1 \ + --no-enforce-gate \ + --max-steps 1 \ + --startup-timeout-s 180 \ + --artifact-dir /tmp/opencode/lerobot-libero-standard-discovery-test-2 +``` + +Result: + +```text +episodes=1 successes=0 success_rate=0.000 passed=False +``` + +This proves sidecar startup, standard LIBERO package asset discovery, runtime +action protocol, and fake backend path work. It is not a policy quality check. + +### Real-backend attempt with PyPI LeRobot + +Command used `--with libero --with lerobot`. + +Result: PyPI `lerobot==0.5.1` installed, but VLA-JEPA import was not usable in +Python 3.12 due an upstream policy-package import/dataclass error. + +### Real-backend attempt with GitHub LeRobot main + +`importlib.util.find_spec('lerobot.policies.vla_jepa.modeling_vla_jepa')` +succeeded with GitHub main (`lerobot==0.5.2`). + +A full gate command then reached checkpoint load but failed because the VLA-JEPA +extra dependency `diffusers` was missing: + +```text +ImportError: 'diffusers' is required but not installed. +Install it with: pip install 'lerobot[vla_jepa]' +``` + +That is why the recommended command now installs `lerobot[vla_jepa]` from GitHub +main. + +## What to inspect after the run + +Top-level artifacts should include: + +```text +summary.json +episodes.jsonl +runtime_description.json +checkpoint_metadata.json +run_config.json +cleanup_status.json +``` + +For success, check: + +```bash +python - <<'PY' +import json +from pathlib import Path +p = Path('artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full/summary.json') +print(json.dumps(json.loads(p.read_text()), indent=2)) +PY +``` + +The benchmark only passes if: + +```text +episodes == 50 +success_rate > 0.50 +passed == true +``` + +With `--save-videos`, per-episode MP4s should appear under: + +```text +artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full/episodes//videos// +``` + +## If it fails remotely + +Do not weaken the gate. Iterate on concrete failure causes until either: + +1. `success_rate > 0.50`, or +2. there is a hard blocker outside repo control. + +Useful places to inspect: + +```text +artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full/setup_error.json +artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full/episodes/*/libero_sidecar.log +artifacts/benchmark/lerobot-vla-jepa-libero-real-gate-full/episodes.jsonl +``` + +Common likely issues: + +- missing VLA-JEPA extra deps: use GitHub main with `[vla_jepa]` +- CUDA/device mismatch: pass `--device cuda` or the correct available device +- long first LIBERO asset download: increase `--startup-timeout-s` +- observation key mismatch: inspect per-episode artifacts + failure reason +- simulator/headless rendering issue: ensure `MUJOCO_GL=egl` or equivalent is + available on the remote machine + +## Reminder + +This gate is complete when a real non-fake run writes a 50-episode `summary.json` +with `success_rate > 0.50`. The verified local artifact currently satisfies that +condition with `success_rate=0.900`. diff --git a/openspec/changes/add-xvla-libero-policy-gate/.openspec.yaml b/openspec/changes/add-xvla-libero-policy-gate/.openspec.yaml new file mode 100644 index 0000000000..dd9a1d92eb --- /dev/null +++ b/openspec/changes/add-xvla-libero-policy-gate/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-06 diff --git a/openspec/changes/add-xvla-libero-policy-gate/design.md b/openspec/changes/add-xvla-libero-policy-gate/design.md new file mode 100644 index 0000000000..4b5303874b --- /dev/null +++ b/openspec/changes/add-xvla-libero-policy-gate/design.md @@ -0,0 +1,130 @@ +## Context + +The archived `add-live-policy-stream-rollout` change established a module-native live policy stream path for LIBERO: + +```text +LIBERO runtime observations + │ + ▼ +RobotPolicyModule.policy_observation + │ + ▼ +async backend chunk inference + │ + ▼ +RobotPolicyModule.policy_action_chunk + │ + ▼ +ControlCoordinator.robot_policy_action_chunk + │ + ▼ +PolicyChunkControlTask → LIBERO native runtime step +``` + +That path is architecturally policy-oriented, but the current concrete LeRobot backend imports VLA-JEPA directly and the live parity gate is named and configured around `lerobot/VLA-JEPA-LIBERO`. X-VLA gives us a useful second policy because it is a separate LeRobot policy class/checkpoint (`lerobot/xvla-libero`) with LIBERO evaluation support. Implementation discovery showed X-VLA does **not** share VLA-JEPA's normalized delta action semantics: it needs X-VLA-specific observation preprocessing, domain id `3`, main-camera 180° rotation, and absolute 7D end-effector pose/axis-angle/gripper actions. + +## Goals / Non-Goals + +**Goals:** + +- Support X-VLA LIBERO as a real LeRobot policy option without replacing the existing VLA-JEPA path. +- Preserve the module-native live topology: deployed runtime module, deployed `RobotPolicyModule`, deployed `ControlCoordinator`, DimOS streams, and RPC refill trigger. +- Keep the policy boundary action-chunk based so ControlCoordinator continues to own receding-horizon execution and stale-action behavior. +- Add a two-stage 10-episode `libero_object` X-VLA acceptance gate: synchronous benchmark first, then module-native live parity, each with `success_rate > 0.50`. +- Keep optional LeRobot/X-VLA dependencies outside the main project dependencies; use the local sidecar/`uv run --with ...` pattern for real gate execution. + +**Non-Goals:** + +- Do not train or fine-tune X-VLA. +- Do not replace the VLA-JEPA gate or lower its acceptance criteria. +- Do not add a new DimOS CLI command. +- Do not make ControlCoordinator policy-specific or teach the policy module physical controller mappings. +- Do not add PI0/PI0Fast as part of this change; those are intentionally excluded because they are too large for the intended local gate. + +## Decisions + +### Decision 1: Treat X-VLA as a second real policy gate, not just a checkpoint swap + +X-VLA should run through the same live policy stream path and produce the same style of `RobotPolicyActionChunk`, but the implementation should not pretend every LeRobot checkpoint uses `VLAJEPAPolicy`. The gate must exercise policy class selection/import, processor setup, metadata, chunk inference, and diagnostics with a non-VLA-JEPA LeRobot policy class. + +Alternatives considered: + +- **Only change `--checkpoint`**: rejected because the current backend imports VLA-JEPA directly and would not prove backend policy agnosticism. +- **Add a completely separate benchmark script**: rejected because it would weaken the topology parity claim. + +### Decision 2: Prefer a configurable LeRobot LIBERO backend seam, with family-specific import adapters + +The backend should support a policy-family selector such as `vla_jepa` or `xvla`, mapping each family to its policy class import, dependency hint, and default checkpoint. This keeps `RobotPolicyModule` configuration stable while acknowledging that LeRobot policy families may live in different modules and require different extras. + +```text +backend_type="lerobot" +backend_params={ + "policy_family": "xvla", + "checkpoint_id": "lerobot/xvla-libero", + "device": "cuda", + "use_action_chunk": true, +} +``` + +Alternatives considered: + +- **Separate `lerobot_xvla` backend type**: simpler first patch, but duplicates processor/device/chunk handling and makes future policy-family additions noisier. +- **Fully dynamic import from checkpoint config only**: attractive long term, but riskier because error messages and optional dependency guidance become less explicit. + +### Decision 3: Use a narrow X-VLA LIBERO contract and absolute native action space + +The current `vla_jepa_libero` contract maps DimOS robot policy observations to LeRobot LIBERO batch keys and validates `libero.ee_delta_6d_gripper.normalized.v1` 7D outputs. Real X-VLA smoke testing showed different semantics: installed LeRobot's checkpoint processor expects precomputed `observation.state` and `domain_id`, X-VLA's LIBERO processor convention flips the main camera by 180°, and postprocessed actions are absolute end-effector pose axis-angle/gripper commands that can legitimately exceed `[-1, 1]`. Therefore X-VLA uses a separate `xvla_libero` contract and `libero.ee_pose_axis_angle_gripper.absolute.v1` action-space id, while the LIBERO runtime uses a `native_absolute` mode that leaves the OSC pose controller in absolute mode. + +Alternatives considered: + +- **Rename the existing contract immediately to `lerobot_libero`**: good eventual direction, but it can hide policy-specific semantic drift unless X-VLA is first verified against the same mapping. +- **Force X-VLA into VLA-JEPA's normalized delta action id**: rejected because it silently mixes incompatible action spaces and collapsed the real benchmark gate to 0/10. +- **Clamp X-VLA actions to `[-1, 1]`**: rejected because it destroys the absolute pose semantics needed by the policy. + +### Decision 4: Use a two-stage X-VLA gate + +Use the same `libero_object` 10-episode slice and hard pass condition `success_rate > 0.50` for two sequential checks: + +1. **Synchronous benchmark stage**: validates that X-VLA can run through the fast lockstep benchmark path, including runtime reset/snapshot, observation assembly, backend inference, action adaptation, scoring, artifacts, and cleanup. +2. **Module-native live stage**: validates that the same policy family can run through deployed `RobotPolicyModule`, stream-delivered observations/chunks, ControlCoordinator refill RPC, and policy chunk task execution. + +The second stage should not be considered acceptance-ready unless the first stage has passed for the same policy family/checkpoint and comparable episode slice. This separates policy/backend/contract correctness from live-control topology correctness and makes failures easier to localize. + +Both stages should write separate artifacts naming the policy family/checkpoint and stage so VLA-JEPA, X-VLA benchmark, and X-VLA live evidence do not overwrite each other. + +### Decision 5: Add smoke validation before the expensive real gate + +Because X-VLA dependency shape, chunk method availability, and processor expectations are less locally proven than VLA-JEPA, implementation should include a fake/unit path plus a short real-policy smoke before the full 10-episode gate: + +1. Import/load smoke: policy class resolves and checkpoint metadata can be described. +2. Contract smoke: one LIBERO observation converts into backend input accepted by processors. +3. Inference smoke: `predict_action_chunk` or equivalent chunk output is finite and shape-compatible. +4. Synchronous benchmark gate: 10 episodes with `success_rate > 0.50`. +5. Module-native live gate: 10 episodes with `success_rate > 0.50`. + +## Risks / Trade-offs + +- **X-VLA optional dependencies differ from VLA-JEPA** → Keep dependency guidance family-specific and run real gates with `uv run --with ...` rather than modifying main `pyproject.toml`. +- **X-VLA does not expose `predict_action_chunk` under the same method name** → Detect method availability in backend tests and fail with a clear unsupported-policy message; do not silently fall back to single-action live execution unless the spec is updated. +- **X-VLA action semantics differ despite 7D LIBERO output** → Use the X-VLA-specific absolute action-space contract and runtime mode; do not reinterpret those actions as normalized deltas. +- **X-VLA is too heavy or locally unstable** → Keep the change scoped so VLA-JEPA remains the primary existing gate; record setup/integration aborts separately from policy failures. +- **A single episode gives misleading signal** → Use the 10-episode gate for acceptance in both benchmark and live stages, not a cherry-picked single episode. +- **Live failures can obscure policy failures** → Require the synchronous benchmark stage first so backend/contract/policy viability is established before diagnosing live-control topology. + +## Migration Plan + +1. Add policy-family selection behind the existing backend/contract registry seams. +2. Add tests and documentation for selecting VLA-JEPA vs X-VLA without changing the live topology. +3. Run existing VLA-JEPA focused tests to confirm no regression. +4. Run X-VLA smoke validation. +5. Run the X-VLA 10-episode `libero_object` synchronous benchmark gate and save artifacts. +6. Run the X-VLA 10-episode `libero_object` live policy stream gate and save artifacts. + +Rollback is straightforward: keep VLA-JEPA code paths and remove or disable the X-VLA family configuration/gate if X-VLA cannot be supported locally. + +## Open Questions + +- Which LeRobot installation extra is required for X-VLA in the current LeRobot release (`xvla`, broader VLA extra, or source install without extra)? +- Does `lerobot/xvla-libero` expose `predict_action_chunk`, or does it require a family-specific multi-step inference method? +- Does the checkpoint-saved processor fully encode X-VLA action mode/domain id, or must DimOS pass explicit X-VLA contract metadata? +- Should the existing `vla_jepa_libero` contract be renamed after X-VLA proves semantic compatibility, or should it remain as-is for backward clarity? diff --git a/openspec/changes/add-xvla-libero-policy-gate/proposal.md b/openspec/changes/add-xvla-libero-policy-gate/proposal.md new file mode 100644 index 0000000000..9e0ceec373 --- /dev/null +++ b/openspec/changes/add-xvla-libero-policy-gate/proposal.md @@ -0,0 +1,31 @@ +## Why + +The current module-native live policy stream gate proves the topology works for `lerobot/VLA-JEPA-LIBERO`, but the concrete LeRobot backend and demo configuration still encode VLA-JEPA assumptions. Adding an X-VLA LIBERO gate gives us a second real policy family using the same RobotPolicyModule → ControlCoordinator live stream path, strengthening the claim that the implementation is policy-agnostic rather than VLA-JEPA-specific. + +## What Changes + +- Add support for loading and running the LeRobot X-VLA LIBERO checkpoint, expected as `lerobot/xvla-libero`, through the policy rollout backend/contract seams. +- Generalize the LeRobot LIBERO backend selection so policy class import, dependency guidance, checkpoint metadata, and inference method are not hardcoded to VLA-JEPA. +- Reuse the LIBERO observation/action contract where X-VLA has the same two-camera, 8D state, language prompt, and 7D normalized action semantics; introduce a separate contract only if X-VLA requires distinct semantic mapping. +- Extend the policy rollout script/configuration to select X-VLA as a real-policy gate candidate without replacing the existing VLA-JEPA gate. +- Add a two-stage X-VLA acceptance gate over the same 10-episode `libero_object` slice: first the synchronous benchmark path, then the module-native live policy stream path, each with `success_rate > 0.50`. +- Keep the synchronous benchmark path and existing VLA-JEPA gates available. + +## Capabilities + +### New Capabilities + +None. + +### Modified Capabilities + +- `robot-policy-module`: Generalize LeRobot backend support beyond VLA-JEPA so RobotPolicyModule can run X-VLA LIBERO through the same backend/contract interfaces and chunk output semantics. +- `benchmark-policy-evaluation`: Add an X-VLA synchronous benchmark gate that verifies the fast lockstep policy evaluation path before live stream validation. +- `runtime-scripted-demos`: Add an X-VLA LIBERO live policy stream parity gate that validates a second real LeRobot policy through the existing module-native rollout path. + +## Impact + +- Affected code: policy rollout backend registry and LeRobot backend, LIBERO policy contract selection if needed, `RobotPolicyModule` configuration tests, and `scripts/benchmarks/demo_lerobot_libero_policy_rollout.py` policy-selection arguments/tests. +- Affected docs/specs: LeRobot LIBERO rollout documentation and OpenSpec requirements for policy module backend support, synchronous benchmark gates, and live parity gates. +- Dependencies: X-VLA may require a LeRobot extra or optional dependency set distinct from `vla_jepa`; setup errors should fail clearly without adding X-VLA dependencies to the main `pyproject.toml`. +- Runtime systems: LIBERO runtime assets remain explicit/prepared; stage 1 uses the existing synchronous benchmark runner, and stage 2 uses the same module-native `LiberoProRuntimeModule`, deployed `RobotPolicyModule`, `ControlCoordinator`, streams, RPC trigger, and policy chunk task. diff --git a/openspec/changes/add-xvla-libero-policy-gate/specs/benchmark-policy-evaluation/spec.md b/openspec/changes/add-xvla-libero-policy-gate/specs/benchmark-policy-evaluation/spec.md new file mode 100644 index 0000000000..9a4d056abe --- /dev/null +++ b/openspec/changes/add-xvla-libero-policy-gate/specs/benchmark-policy-evaluation/spec.md @@ -0,0 +1,20 @@ +## ADDED Requirements + +### Requirement: X-VLA LIBERO synchronous benchmark gate +The system SHALL include an X-VLA LIBERO synchronous benchmark gate that validates `lerobot/xvla-libero` through the fast lockstep benchmark policy evaluation path before module-native live stream acceptance is considered complete. + +#### Scenario: X-VLA benchmark gate runs the real policy +- **WHEN** a developer runs the X-VLA LIBERO synchronous benchmark gate with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the gate loads the actual `lerobot/xvla-libero` policy rather than using fake, fixed, or VLA-JEPA policy actions as the acceptance path + +#### Scenario: X-VLA benchmark gate preserves lockstep evaluation ownership +- **WHEN** the X-VLA synchronous benchmark gate evaluates an episode +- **THEN** benchmark evaluation owns runtime reset, stream snapshot collection, policy inference call, action adaptation, runtime step, scoring, artifacts, and cleanup without requiring ControlCoordinator policy chunk execution + +#### Scenario: X-VLA benchmark gate enforces 10-episode success +- **WHEN** the 10-episode X-VLA synchronous benchmark gate over `libero_object` completes without setup or contract aborts +- **THEN** the gate passes only if the recorded success rate is greater than `0.50` + +#### Scenario: X-VLA benchmark artifacts identify policy and stage +- **WHEN** the X-VLA synchronous benchmark gate runs +- **THEN** it writes rollout artifacts that identify the X-VLA policy family, checkpoint, benchmark stage, episode count, success count, success rate, threshold, and pass/fail result diff --git a/openspec/changes/add-xvla-libero-policy-gate/specs/robot-policy-module/spec.md b/openspec/changes/add-xvla-libero-policy-gate/specs/robot-policy-module/spec.md new file mode 100644 index 0000000000..ad6fd761f7 --- /dev/null +++ b/openspec/changes/add-xvla-libero-policy-gate/specs/robot-policy-module/spec.md @@ -0,0 +1,27 @@ +## ADDED Requirements + +### Requirement: LeRobot backend supports X-VLA LIBERO policy +The system SHALL allow RobotPolicyModule to load and run a LeRobot X-VLA LIBERO policy through the same policy backend interface used by existing LIBERO policy rollout paths. + +#### Scenario: Backend initializes X-VLA checkpoint +- **WHEN** a robot policy module is configured with the LeRobot backend, the X-VLA policy family, and the `lerobot/xvla-libero` checkpoint +- **THEN** the backend loads the X-VLA policy through its LeRobot policy API, prepares LeRobot preprocessing and postprocessing through official processor factories, moves the policy to the configured device, and enters inference/evaluation mode + +#### Scenario: Backend reports X-VLA metadata +- **WHEN** the X-VLA backend is initialized or described for rollout artifacts +- **THEN** the backend description includes the configured checkpoint identifier, resolved policy class, device, policy family, episode reset support, and chunk inference support + +#### Scenario: Missing X-VLA dependency fails clearly +- **WHEN** X-VLA policy loading fails because the required LeRobot policy package or optional dependency is unavailable +- **THEN** backend construction or initialization fails with an actionable message naming the X-VLA policy family and the dependency setup needed to run it + +### Requirement: X-VLA LIBERO chunk inference preserves policy boundary semantics +The system SHALL convert X-VLA LIBERO policy outputs into runtime-independent robot policy action chunks before any runtime or ControlCoordinator execution mapping. + +#### Scenario: X-VLA chunk output is published as robot policy chunk +- **WHEN** X-VLA live inference produces a finite LIBERO absolute end-effector pose chunk compatible with X-VLA's 7D axis-angle/gripper action surface +- **THEN** RobotPolicyModule publishes a `RobotPolicyActionChunk` with action-space id `libero.ee_pose_axis_angle_gripper.absolute.v1`, ordered action values, policy metadata, and no controller-specific command mapping + +#### Scenario: X-VLA semantic mismatch is rejected before execution +- **WHEN** X-VLA backend output has an incompatible shape, non-finite value, or unsupported action-space semantics +- **THEN** the robot policy contract rejects the output before ControlCoordinator receives an executable policy chunk diff --git a/openspec/changes/add-xvla-libero-policy-gate/specs/runtime-scripted-demos/spec.md b/openspec/changes/add-xvla-libero-policy-gate/specs/runtime-scripted-demos/spec.md new file mode 100644 index 0000000000..5e8302bde9 --- /dev/null +++ b/openspec/changes/add-xvla-libero-policy-gate/specs/runtime-scripted-demos/spec.md @@ -0,0 +1,24 @@ +## ADDED Requirements + +### Requirement: X-VLA LIBERO live policy stream parity gate +The system SHALL include a script-based X-VLA LIBERO live policy stream parity gate that validates `lerobot/xvla-libero` through the same module-native RobotPolicyModule live chunk inference, ControlCoordinator policy chunk execution, LIBERO runtime observation streams, score collection, artifact output, and teardown used by the VLA-JEPA live gate after the X-VLA synchronous benchmark stage has passed. + +#### Scenario: X-VLA gate runs the real policy +- **WHEN** a developer runs the X-VLA LIBERO live policy stream parity gate with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the gate loads the actual `lerobot/xvla-libero` policy rather than using fake, fixed, or VLA-JEPA policy actions as the acceptance path + +#### Scenario: X-VLA gate uses existing live topology +- **WHEN** the X-VLA live parity gate executes policy actions +- **THEN** observations flow into RobotPolicyModule, inferred X-VLA robot policy action chunks flow through ControlCoordinator, and the policy chunk control task drives the runtime control path without adding X-VLA-specific control execution logic + +#### Scenario: X-VLA gate enforces 10-episode real-policy success +- **WHEN** the 10-episode X-VLA live parity gate over `libero_object` completes without setup or contract aborts +- **THEN** the gate passes only if the recorded success rate is greater than `0.50` + +#### Scenario: X-VLA live gate is second acceptance stage +- **WHEN** X-VLA acceptance is evaluated for policy-agnostic rollout support +- **THEN** the live parity gate runs as the second stage after a passing X-VLA synchronous benchmark gate for the same policy family and checkpoint + +#### Scenario: X-VLA artifacts identify policy family and diagnostics +- **WHEN** the X-VLA LIBERO live parity gate runs +- **THEN** it writes rollout artifacts that identify the X-VLA policy family, checkpoint, and live stage and include live-path diagnostics such as chunk counts, refill triggers, inference status counts, consumed actions, stale deactivations, and cleanup status diff --git a/openspec/changes/add-xvla-libero-policy-gate/tasks.md b/openspec/changes/add-xvla-libero-policy-gate/tasks.md new file mode 100644 index 0000000000..87a1e7ee59 --- /dev/null +++ b/openspec/changes/add-xvla-libero-policy-gate/tasks.md @@ -0,0 +1,38 @@ +## 1. X-VLA backend capability + +- [x] 1.1 Add a LeRobot policy-family selection seam that preserves the existing VLA-JEPA default while allowing `policy_family="xvla"`. +- [x] 1.2 Add X-VLA policy class import/loader support with a family-specific dependency/setup error message. +- [x] 1.3 Ensure backend metadata reports policy family, checkpoint id, resolved policy class, device, episode reset support, and chunk inference support for X-VLA. +- [x] 1.4 Preserve existing VLA-JEPA backend behavior and tests while adding focused unit tests for X-VLA family selection and clear missing-dependency failures. + +## 2. X-VLA LIBERO contract and chunk semantics + +- [x] 2.1 Verify whether `lerobot/xvla-libero` uses the existing LIBERO observation keys, language metadata, state shape, and action surface; record that X-VLA requires a separate 20D state and absolute 7D action surface. +- [x] 2.2 Reuse the existing LIBERO contract if X-VLA semantics match; otherwise add a narrow X-VLA LIBERO contract through the contract registry. +- [x] 2.3 Add contract tests proving finite X-VLA `N x 7` chunk output converts to `RobotPolicyActionChunk` with action-space id `libero.ee_pose_axis_angle_gripper.absolute.v1`. +- [x] 2.4 Add rejection tests for incompatible X-VLA output shape, non-finite values, or unsupported action semantics. + +## 3. Script and configuration surface + +- [x] 3.1 Extend `scripts/benchmarks/demo_lerobot_libero_policy_rollout.py` so callers can select VLA-JEPA or X-VLA policy family/checkpoint without changing the live topology. +- [x] 3.2 Add script/configuration support for a two-stage X-VLA gate: synchronous benchmark stage followed by module-native live stage. +- [x] 3.3 Ensure fake/fixed backend tests continue to cover module-native live stream wiring without requiring X-VLA dependencies. +- [x] 3.4 Add script tests proving X-VLA configuration can run the synchronous benchmark path without ControlCoordinator policy chunk execution. +- [x] 3.5 Add script tests proving X-VLA configuration deploys `RobotPolicyModule`, `ControlCoordinator`, stream remapping, and policy trigger RPC through the same live path. +- [x] 3.6 Ensure X-VLA artifacts include policy family, checkpoint id, stage name, inference method/status counts when applicable, chunk counts, consumed actions, stale deactivations, and cleanup status. + +## 4. Smoke validation and two-stage real gate + +- [x] 4.1 Document the X-VLA dependency invocation pattern without adding X-VLA dependencies to the main `pyproject.toml`. +- [x] 4.2 Run an X-VLA import/load smoke that verifies policy class resolution, checkpoint description, processor setup, and device placement. +- [x] 4.3 Run an X-VLA inference smoke that verifies finite chunk output with shape compatible with the LIBERO 7D action surface. +- [x] 4.4 Run the 10-episode `libero_object` X-VLA synchronous benchmark gate with pass condition `success_rate > 0.50`. +- [ ] 4.5 Run the 10-episode `libero_object` X-VLA module-native live policy stream gate with pass condition `success_rate > 0.50` after the benchmark stage passes. +- [ ] 4.6 Save and reference both X-VLA gate artifact directories, including the passing benchmark artifact with videos at `artifacts/benchmark/lerobot-xvla-libero-benchmark-10-image-flip-videos/` and the pending live-path diagnostics. + +## 5. Regression validation and docs + +- [x] 5.1 Run existing focused VLA-JEPA policy rollout tests to confirm no regression in the current gate. +- [x] 5.2 Run X-VLA-focused backend, contract, policy module, policy chunk task, and benchmark script tests. +- [x] 5.3 Run OpenSpec validation for `add-xvla-libero-policy-gate` in strict mode. +- [x] 5.4 Update developer documentation to describe VLA-JEPA vs X-VLA policy selection, local dependency setup, and expected gate commands. diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/.openspec.yaml b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/.openspec.yaml new file mode 100644 index 0000000000..d6b53dee55 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-30 diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/design.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/design.md new file mode 100644 index 0000000000..bdc80a10ba --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/design.md @@ -0,0 +1,226 @@ +## Context + +DimOS already has runtime sidecar infrastructure and a LIBERO sidecar package that can run registered LIBERO tasks behind a lightweight HTTP protocol. The current LIBERO demo proves reset/step/scoring, camera payload export, a local SHM motor plane, and ControlCoordinator-driven joint-position commands. + +That path is not enough to verify a learned policy. Official LeRobot LIBERO checkpoints, including `lerobot/VLA-JEPA-LIBERO`, use LIBERO's native relative end-effector-delta-plus-gripper action mode rather than the current Panda joint-position motor surface. The policy rollout architecture therefore needs two separate but cooperating pieces: + +1. a robot-learning rollout stack that owns policy lifecycle, contract conversion, and episode execution; and +2. a runtime sidecar native action mode that can accept LIBERO-native actions without pretending they are motor commands. + +This change intentionally bypasses ControlCoordinator for v1 policy rollout. The ControlCoordinator action-surface gap is documented separately in `openspec/changes/support-control-coordinator-action-surfaces/control-coordinator-action-surface-gap.md` and should be handled by a later change. + +## Goals / Non-Goals + +**Goals:** + +- Add a first `RobotPolicyModule` shape for policy inference and action emission. +- Add a separate benchmark/evaluation runner for episode matrix, runtime lifecycle, scoring, and artifacts. +- Add a batch-first `PolicyBackend` abstraction and an in-process `LeRobotBackend` for `lerobot/VLA-JEPA-LIBERO`. +- Add a narrow `VlaJepaLiberoRobotContract` that converts between LIBERO sidecar observations, LeRobot backend batches, backend outputs, and native runtime actions. +- Extend the runtime protocol with a native runtime action frame while preserving existing motor action frames. +- Extend the LIBERO sidecar with native LIBERO action mode for official relative end-effector delta plus gripper actions. +- Provide a 50-episode policy gate over `libero_object`, all 10 task indices, init states `[0, 1, 2, 3, 4]`, with `success_rate > 0.50`. +- Emit enough metadata and artifacts to debug policy, contract, sidecar, and version mismatches. + +**Non-Goals:** + +- No ControlCoordinator dispatch for v1 policy rollout. +- No `JointTrajectoryTask` or `EndEffectorDeltaTrajectoryTask` implementation in this change. +- No EE-delta-to-joint conversion. +- No overloading `MotorActionFrame` for end-effector actions. +- No dynamic observation binding/schema system. +- No general multi-policy registry or universal robot contract framework. +- No mandatory `lerobot-eval` comparison in the acceptance gate. +- No strict dependency version pinning beyond recording versions and resolved checkpoint metadata. + +## Decisions + +### 1. Split RobotPolicyModule from benchmark lifecycle + +The demo must exercise the intended robot-learning architecture rather than directly calling LeRobot from a one-off script. However, the policy module should not own benchmark lifecycle. The v1 architecture should split responsibilities: + +- `RobotPolicyModule` owns policy backend lifecycle, public policy reset, observation-to-action inference, and action emission. +- `BenchmarkPolicyEvalRunner` owns episode matrix selection, sidecar lifecycle, sidecar reset/step calls, scoring, success gate, metrics, artifacts, and cleanup. + +`RobotPolicyModule` should expose a public reset method because LeRobot policies can keep episode-local state such as action queues or temporal context. + +The core flow is: + +```text +BenchmarkPolicyEvalRunner + -> RuntimeSidecarClient.reset(...) + -> RobotLearningSample / sidecar observation wrapper + -> RobotPolicyModule.reset(...) at episode boundary + -> RobotPolicyModule.infer_action(sample) + -> VlaJepaLiberoRobotContract.to_backend_batch(...) + -> LeRobotBackend.infer_batch(...) + -> VlaJepaLiberoRobotContract.from_backend_output(...) + -> RuntimeSidecarClient.step(RuntimeActionFrame) +``` + +Alternative considered: implement the gate as a plain LeRobot evaluation script. That would be faster but would not validate the DimOS policy-module/evaluation-runner/backend/contract seams. + +### 2. Use a batch-first PolicyBackend interface + +`PolicyBackend` should expose a batch-oriented inference method: + +```python +class PolicyBackend(Protocol): + def initialize(self) -> None: ... + def reset_episode(self) -> None: ... + def infer_batch(self, batch: BackendBatch) -> BackendOutputEnvelope: ... + def close(self) -> None: ... + def describe(self) -> PolicyBackendDescription: ... +``` + +`select_action` is not the rollout contract. `LeRobotBackend` may use LeRobot's `select_action`, `predict_action_chunk`, processor pipeline, or action queue internally, but `RobotPolicyModule` should only depend on `infer_batch(...)`. + +Rationale: LeRobot, starVLA, and Dexbotic-like deployments all converge on explicit observation/action batch or capability surfaces. A batch-first backend also keeps training/inference IO conventions visible at the contract boundary. + +### 3. Keep RobotContract as the semantic IO boundary + +The v1 contract is backend-specific and runtime-specific: + +```text +VlaJepaLiberoRobotContract +``` + +It owns semantic conversion: + +```text +sidecar agentview -> observation.images.image +sidecar eye-in-hand -> observation.images.image2 +sidecar 8D robot_state -> observation.state +sidecar language -> task prompt + +LeRobot backend output -> RuntimeActionFrame(space_id="libero.ee_delta_6d_gripper.normalized.v1", values=float32[7]) +``` + +It also validates semantic mismatches and fails fast on missing observations, wrong shapes, wrong dtypes, invalid action shapes, non-finite values, or incompatible action range. + +The contract does not load policies, schedule inference, call sidecar step, write artifacts, or know about ControlCoordinator. + +### 4. Extend StepRequest with a RuntimeActionFrame union + +Runtime protocol `StepRequest.action` should become a discriminated union: + +```text +MotorActionFrame | RuntimeActionFrame +``` + +`RuntimeActionFrame` should carry a semantic action surface identity and values, for example: + +```text +kind: "runtime_action" +space_id: "libero.ee_delta_6d_gripper.normalized.v1" +values: float32[7] +sequence: int +``` + +Rationale: one `/step` operation remains the runtime stepping concept, while individual sidecar modes can accept or reject action frame kinds. This avoids adding parallel step endpoints and avoids lying through `MotorActionFrame.q`. + +### 5. Add native LIBERO action mode to the sidecar + +The existing sidecar joint-position motor mode remains unchanged. Native LIBERO action mode should follow the official LeRobot LIBERO environment setup and validate the resulting environment action spec instead of hardcoding a guessed simulator controller string. + +Native mode should: + +- construct the LIBERO environment as official LeRobot LIBERO evaluation expects; +- inspect `env.action_spec`; +- require action dimension `(7,)`; +- require bounds compatible with `[-1, 1]`; +- advertise the native action surface and action mode in runtime description metadata; +- accept `RuntimeActionFrame` with matching `space_id`; and +- reject `MotorActionFrame` in native mode. + +The term should be "LIBERO action mode", not "controller", because controller is overloaded with DimOS ControlCoordinator and control tasks. + +### 6. Use an in-process LeRobotBackend for v1 + +`LeRobotBackend` runs in the DimOS policy rollout process for v1. It owns: + +- checkpoint loading for `lerobot/VLA-JEPA-LIBERO`; +- policy/device initialization; +- LeRobot processor/preprocessor/postprocessor setup as required by the checkpoint; +- `policy.reset()` or equivalent episode reset; +- inference under no-grad/inference mode; +- conversion of raw backend output into `BackendOutputEnvelope`; and +- metadata for artifacts. + +Future worker/venv isolation can be added later if dependency conflicts require it. The v1 abstraction seam is `PolicyBackend`, not a separate process boundary. + +### 7. Define the 50-episode gate as a sidecar-native rollout + +The gate uses the DimOS policy rollout stack, not `lerobot-eval`: + +```text +checkpoint: lerobot/VLA-JEPA-LIBERO +suite: libero_object +task_indices: all 10 tasks +init_state_indices: [0, 1, 2, 3, 4] +episodes: 50 +pass: successes / 50 > 0.50 +``` + +`lerobot-eval` is only a debugging fallback if the DimOS path underperforms and the team needs to separate policy/checkpoint problems from DimOS sidecar/contract problems. + +### 8. Treat setup/contract errors differently from policy failures + +Policy failures count as episode failures and allow the run to continue: + +- `success=false`; +- timeout; +- done without success. + +Setup and contract failures abort the whole demo run: + +- missing observation stream; +- wrong image/state shape; +- action spec mismatch; +- invalid action dtype/shape/range; +- checkpoint/backend load failure; +- sidecar protocol mismatch. + +Rationale: contract/setup errors mean the integration is wired incorrectly; continuing 50 episodes would only generate noisy false failures. + +### 9. Record metadata by default and videos only when requested + +Always write: + +- `summary.json`; +- `episodes.jsonl`; +- `runtime_description.json`; +- `contract_description.json`; and +- `checkpoint_metadata.json`. + +Per episode, record task/init identity, success, steps, reward sum, done, failure reason, action min/max/shape, and observation streams. + +Add `--save-videos` for optional rollout video artifacts. Do not save full images/videos by default because 50 episodes can become large quickly. + +## Risks / Trade-offs + +- **LeRobot observation mismatch** → Mirror the official VLA-JEPA LIBERO observation mapping in one hardcoded contract and fail fast on missing cameras/state. +- **LIBERO action-mode mismatch** → Inspect and validate `env.action_spec` in native mode before accepting step requests. +- **LeRobot dependency drift** → Record checkpoint revision and key package versions in artifacts; avoid strict pins until implementation proves the environment path. +- **Artifact bloat** → Default to JSON metadata and make videos opt-in via `--save-videos`. +- **Bypassing ControlCoordinator may look inconsistent with real-robot goals** → Keep the action-surface gap documented as a separate change and explicitly scope this change to policy/backend/runtime verification. +- **In-process LeRobot may conflict with DimOS dependencies** → Use `PolicyBackend` as the seam; move to a venv/worker backend later if necessary. + +## Migration Plan + +1. Extend runtime protocol types while preserving `MotorActionFrame` behavior. +2. Add sidecar native LIBERO action mode behind explicit configuration, keeping joint-position mode unchanged. +3. Add stubbed sidecar tests for native action frame validation without real LIBERO assets. +4. Add `PolicyBackend`, `LeRobotBackend`, `VlaJepaLiberoRobotContract`, `RobotPolicyModule`, and `BenchmarkPolicyEvalRunner` code paths. +5. Add a minimal sidecar-native smoke path with valid fixed actions before LeRobot inference. +6. Add one-episode policy smoke coverage where real dependencies are available. +7. Add the 50-episode manual/optional gate and artifact writing. + +Rollback is straightforward: existing motor-frame demos remain separate, and the new policy demo can be disabled or skipped without changing the existing LIBERO joint-position workflow. + +## Open Questions + +- Exact source location and package boundaries for the first `RobotPolicyModule`, `BenchmarkPolicyEvalRunner`, `PolicyBackend`, and contract classes. +- Exact LeRobot processor API calls needed by the installed LeRobot version for `lerobot/VLA-JEPA-LIBERO`. +- Exact official camera stream names for VLA-JEPA LIBERO in the current LeRobot/LIBERO stack, especially the wrist/eye-in-hand name. +- Whether the native action mode should be added to the existing LIBERO-PRO sidecar name/package or trigger a later package rename once non-PRO LIBERO usage is clearer. diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/proposal.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/proposal.md new file mode 100644 index 0000000000..fb8b0983cd --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/proposal.md @@ -0,0 +1,37 @@ +## Why + +DimOS has a LIBERO runtime sidecar workflow, but it only proves scripted joint/motor plumbing; it does not yet prove that a real robot-learning policy can load observations, run inference, and drive a benchmark episode through the runtime boundary. Official LeRobot LIBERO checkpoints provide a concrete high-signal target for validating a first policy rollout module, backend boundary, and runtime action path. + +## What Changes + +- Add a first robot-learning policy rollout path centered on a `RobotPolicyModule`, a separate benchmark/evaluation runner, a batch-first `PolicyBackend` interface, a `LeRobotBackend`, and a narrow `VlaJepaLiberoRobotContract`. +- Extend the runtime protocol so a step request can carry either the existing motor action frame or a native runtime action frame. +- Extend the LIBERO runtime sidecar with a native LIBERO action mode that follows the official LeRobot LIBERO action setup and accepts `float32[7]` relative end-effector delta plus gripper actions. +- Add a policy-driven LIBERO demo/gate using `lerobot/VLA-JEPA-LIBERO` over `libero_object`, all 10 task indices, init states `[0, 1, 2, 3, 4]`, for 50 total episodes and `success_rate > 0.50`. +- Record structured artifacts for rollout provenance, per-episode results, runtime description, contract description, checkpoint metadata, and optional videos. +- Keep the existing joint-position LIBERO demo path intact. +- Explicitly defer ControlCoordinator action-surface support; v1 bypasses ControlCoordinator for policy rollout because the current coordinator execution model is joint/motor oriented. + +## Capabilities + +### New Capabilities +- `robot-policy-module`: Defines reusable robot policy inference/action emission, including public reset, batch-first backend inference, robot policy contract conversion, and LeRobot backend integration. +- `benchmark-policy-evaluation`: Defines benchmark lifecycle orchestration for policy evaluation, including episode matrix execution, runtime reset/step ownership, scoring, artifacts, and the 50-episode LIBERO policy gate. + +### Modified Capabilities +- `runtime-protocol`: Add native runtime action frames and allow step requests to carry either motor action frames or runtime action frames while preserving backend-neutral protocol boundaries. +- `runtime-libero-pro-sidecar`: Add native LIBERO action mode alongside the existing joint-position motor mode, validating official LIBERO action specs and accepting runtime action frames for native policy rollout. +- `runtime-scripted-demos`: Add a policy-driven LIBERO rollout demo/gate that validates real policy success rather than only scripted runtime plumbing. + +## Impact + +- Affected packages and modules: + - `packages/dimos-runtime-protocol` + - `packages/dimos-libero-pro-sidecar` + - `dimos/simulation/runtime_client` + - new robot-learning policy rollout/backend/contract code under the DimOS Python package + - benchmark/demo scripts and tests under `scripts/benchmarks` and `dimos/benchmark/runtime` +- Adds an optional LeRobot dependency path for the policy backend environment. +- Requires prepared LIBERO assets for the real 50-episode gate; always-on tests should use stubs and import-boundary checks. +- Does not change the existing motor-frame sidecar demo behavior. +- Does not implement ControlCoordinator non-joint action-surface execution in this change. diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/benchmark-policy-evaluation/spec.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/benchmark-policy-evaluation/spec.md new file mode 100644 index 0000000000..a5431517c8 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/benchmark-policy-evaluation/spec.md @@ -0,0 +1,46 @@ +## ADDED Requirements + +### Requirement: Benchmark policy evaluation runner +The system SHALL provide a benchmark policy evaluation runner that owns episode matrix selection, runtime sidecar lifecycle, runtime reset and step calls, scoring, success gates, metrics, artifacts, and cleanup for policy-driven benchmark rollouts. + +#### Scenario: Evaluation runner uses robot policy module for actions +- **WHEN** the evaluation runner has a current sidecar observation during an episode +- **THEN** it passes the corresponding robot learning sample to the robot policy module and uses the returned runtime action frame for the sidecar step call + +#### Scenario: Evaluation runner owns episode lifecycle +- **WHEN** the evaluation runner starts an episode +- **THEN** it resets the runtime sidecar, calls the robot policy module public reset method, steps the runtime with emitted actions, records episode metrics, and applies the success gate outside the robot policy module + +### Requirement: LIBERO policy rollout gate +The system SHALL provide a policy-driven LIBERO gate using `lerobot/VLA-JEPA-LIBERO` over `libero_object`, all 10 task indices, and init states `[0, 1, 2, 3, 4]`, for 50 total episodes with pass condition `success_rate > 0.50`. + +#### Scenario: Gate evaluates the configured episode matrix +- **WHEN** the policy rollout gate is run with prepared LIBERO assets and compatible LeRobot dependencies +- **THEN** it evaluates 50 episodes formed from all 10 `libero_object` task indices crossed with init states `[0, 1, 2, 3, 4]` + +#### Scenario: Gate records pass/fail summary +- **WHEN** all scheduled episodes complete without setup or contract aborts +- **THEN** the gate writes a summary containing episode count, success count, success rate, threshold, and pass/fail result + +#### Scenario: Policy failures continue the run +- **WHEN** an individual episode times out, reaches done without success, or reports unsuccessful completion +- **THEN** the episode is recorded as a failed policy episode and the rollout continues with the next scheduled episode + +#### Scenario: Setup and contract errors abort the run +- **WHEN** checkpoint loading, sidecar compatibility, action spec validation, observation mapping, action conversion, or protocol validation fails +- **THEN** the rollout aborts the run and records the failure as an integration/setup error rather than counting it as a policy episode failure + +### Requirement: Rollout artifacts +The system SHALL write structured rollout artifacts that make policy, contract, runtime, and episode behavior inspectable without storing full videos or image dumps by default. + +#### Scenario: Required metadata artifacts are written +- **WHEN** a policy rollout gate starts or completes +- **THEN** it writes `summary.json`, `episodes.jsonl`, `runtime_description.json`, `contract_description.json`, and `checkpoint_metadata.json` in the artifact directory + +#### Scenario: Episode records include rollout diagnostics +- **WHEN** an episode record is written +- **THEN** it includes task index, init state index, episode id, success, step count, reward sum, done state, failure reason when present, action shape, action min/max, and observed stream names + +#### Scenario: Videos are opt-in +- **WHEN** the rollout is run without a video-saving option +- **THEN** it does not write full rollout videos or complete image dumps by default diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/robot-policy-module/spec.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/robot-policy-module/spec.md new file mode 100644 index 0000000000..2141d825cb --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/robot-policy-module/spec.md @@ -0,0 +1,49 @@ +## ADDED Requirements + +### Requirement: Robot policy module inference boundary +The system SHALL provide a robot policy module that owns policy backend lifecycle, public policy reset, robot policy contract conversion, backend inference, and policy action emission without owning benchmark episode lifecycle, runtime reset or step calls, scoring, success gates, or artifact writing. + +#### Scenario: Robot policy module emits one action through the architecture seams +- **WHEN** the robot policy module receives a ready robot learning sample and has an initialized policy backend +- **THEN** it converts the sample through the robot policy contract, invokes the backend through `infer_batch`, converts the backend output back through the contract, and returns or emits the resulting runtime action frame + +#### Scenario: Robot policy module exposes public reset +- **WHEN** a benchmark runner starts a new policy episode +- **THEN** it can call the robot policy module public reset method, and the robot policy module resets backend episode state before the next inference + +### Requirement: Batch-first policy backend +The system SHALL define a policy backend interface whose primary inference method accepts backend-ready batches and returns backend output envelopes. + +#### Scenario: Robot policy module does not depend on LeRobot select_action +- **WHEN** the robot policy module needs a policy action +- **THEN** it calls the backend batch inference method rather than depending on LeRobot-specific `select_action` or `predict_action_chunk` APIs + +#### Scenario: Backend describes loaded policy metadata +- **WHEN** a policy backend is initialized +- **THEN** it can describe backend type, checkpoint identifier, resolved checkpoint metadata when available, device, policy class when available, and episode reset support for rollout artifacts + +### Requirement: LeRobot backend for VLA-JEPA LIBERO +The system SHALL provide an in-process LeRobot backend capable of loading and running the official `lerobot/VLA-JEPA-LIBERO` checkpoint for LIBERO policy rollout. + +#### Scenario: Backend initializes official checkpoint +- **WHEN** the LeRobot backend is configured with `lerobot/VLA-JEPA-LIBERO` +- **THEN** it loads the policy, prepares required LeRobot preprocessing or postprocessing, moves the policy to the configured device, and enters inference/evaluation mode + +#### Scenario: Backend returns an output envelope +- **WHEN** the LeRobot backend completes inference for a backend-ready batch +- **THEN** it returns a backend output envelope containing the backend-native action result and inference metadata needed for validation and artifacts + +### Requirement: VLA-JEPA LIBERO robot policy contract +The system SHALL provide a narrow robot policy contract for the VLA-JEPA LIBERO rollout that converts sidecar observations into LeRobot backend batches and converts LeRobot backend output into a native runtime action frame. + +#### Scenario: Contract maps sidecar observation to LeRobot batch +- **WHEN** the contract receives a ready LIBERO sidecar observation for VLA-JEPA LIBERO rollout +- **THEN** it maps agent-view camera data to `observation.images.image`, wrist or eye-in-hand camera data to `observation.images.image2`, the 8D robot state to `observation.state`, and task language to the backend prompt field expected by the backend path + +#### Scenario: Contract rejects semantic input mismatch +- **WHEN** a supposedly ready sample is missing a required stream or has incompatible image, state, dtype, or shape semantics +- **THEN** the contract raises a contract conversion failure before backend inference runs + +#### Scenario: Contract converts backend output to runtime action +- **WHEN** the contract receives a backend output envelope containing a valid VLA-JEPA LIBERO action +- **THEN** it returns a runtime action frame with `space_id` `libero.ee_delta_6d_gripper.normalized.v1` and finite `float32[7]` values compatible with the native LIBERO action range diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-libero-pro-sidecar/spec.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-libero-pro-sidecar/spec.md new file mode 100644 index 0000000000..3773c5fe6a --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-libero-pro-sidecar/spec.md @@ -0,0 +1,44 @@ +## ADDED Requirements + +### Requirement: Native LIBERO action mode +The LIBERO sidecar SHALL support a native LIBERO action mode that follows the official LeRobot LIBERO action setup for relative end-effector delta plus gripper actions. + +#### Scenario: Native action mode validates environment action spec +- **WHEN** the sidecar starts in native LIBERO action mode +- **THEN** it inspects the LIBERO environment action spec and requires action dimension `(7,)` with bounds compatible with `[-1, 1]` + +#### Scenario: Native action mode is described +- **WHEN** the sidecar description is requested in native LIBERO action mode +- **THEN** it reports the native action surface identifier, action shape, action bounds, action mode metadata, task metadata, language, horizon, and camera configuration + +#### Scenario: Native action mode accepts runtime action frame +- **WHEN** DimOS sends a runtime action frame with `space_id` `libero.ee_delta_6d_gripper.normalized.v1` and valid `float32[7]` values +- **THEN** the sidecar maps the values directly to the LIBERO environment step action and returns observations, reward, done, and success metadata + +#### Scenario: Native action mode rejects motor frame +- **WHEN** the sidecar is running in native LIBERO action mode and receives a motor action frame +- **THEN** it rejects the step request with a clear protocol error + +### Requirement: Native LIBERO observation export for policy rollout +The LIBERO sidecar SHALL export the observations needed by the VLA-JEPA LIBERO policy contract when running native LIBERO action mode. + +#### Scenario: Policy observations are available after reset +- **WHEN** the sidecar resets a registered task in native LIBERO action mode +- **THEN** the reset response includes agent-view camera observation metadata, wrist or eye-in-hand camera observation metadata when available, robot state observation metadata, and task language metadata for contract conversion + +#### Scenario: Policy observations are available after step +- **WHEN** the sidecar completes a native runtime action step +- **THEN** the step response includes updated camera and robot state observations needed for the next policy inference tick + +## MODIFIED Requirements + +### Requirement: LIBERO-PRO motor surface validation +The LIBERO-PRO sidecar SHALL expose the full-control motor-frame path only when the selected task and LIBERO action mode provide a Panda joint-position plus gripper whole-body motor surface compatible with DimOS motor action frames. + +#### Scenario: Compatible motor surface is described +- **WHEN** the selected LIBERO-PRO environment exposes the expected Panda joint-position plus gripper action surface for motor-frame mode +- **THEN** the runtime description reports a stable ordered motor surface with supported position command mode and the expected motor count + +#### Scenario: Incompatible motor mode fails fast +- **WHEN** motor-frame mode is selected but the LIBERO environment exposes only a native end-effector action surface or an action dimension that cannot be mapped to Panda joint-position plus gripper commands +- **THEN** the sidecar rejects the episode setup with a clear protocol error before accepting motor-frame step requests diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-protocol/spec.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-protocol/spec.md new file mode 100644 index 0000000000..8d0c581779 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-protocol/spec.md @@ -0,0 +1,27 @@ +## ADDED Requirements + +### Requirement: Native runtime action frames +The runtime protocol SHALL define a native runtime action frame for benchmark action surfaces that are not DimOS motor or joint command surfaces. + +#### Scenario: Runtime action frame identifies semantic action surface +- **WHEN** a runtime action frame is serialized +- **THEN** it includes a discriminator, semantic action surface identifier, numeric action values, and sequence or tick identity without requiring motor names, motor command modes, gains, or joint position fields + +#### Scenario: Runtime action frame validates numeric action values +- **WHEN** a runtime action frame contains non-finite values or values that cannot be parsed as a numeric vector +- **THEN** protocol validation rejects the frame before backend-specific step logic runs + +### Requirement: Step request action frame union +The runtime protocol SHALL allow a step request to carry either a motor action frame or a native runtime action frame while preserving explicit frame discrimination. + +#### Scenario: Motor step request remains valid +- **WHEN** an existing client sends a step request with a valid motor action frame +- **THEN** protocol validation accepts the request as a motor-frame step request + +#### Scenario: Native runtime step request is valid +- **WHEN** a client sends a step request with a valid native runtime action frame +- **THEN** protocol validation accepts the request as a runtime-action step request + +#### Scenario: Ambiguous action frame is rejected +- **WHEN** a step request action lacks a supported discriminator or mixes incompatible motor-frame and runtime-action-frame fields +- **THEN** protocol validation rejects the request before it reaches sidecar step logic diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-scripted-demos/spec.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-scripted-demos/spec.md new file mode 100644 index 0000000000..c420724c55 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/specs/runtime-scripted-demos/spec.md @@ -0,0 +1,24 @@ +## ADDED Requirements + +### Requirement: LeRobot LIBERO policy rollout demo +The system SHALL include a script-based or module-backed LeRobot LIBERO policy rollout demo that validates policy loading, contract conversion, native runtime actions, sidecar stepping, score collection, artifact output, and teardown. + +#### Scenario: Policy rollout demo starts native sidecar and policy rollout stack +- **WHEN** a developer runs the LeRobot LIBERO policy rollout demo with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the demo starts the LIBERO sidecar in native LIBERO action mode, initializes the benchmark evaluation runner with a robot policy module, LeRobot backend, and VLA-JEPA LIBERO contract, runs the configured episode matrix, writes artifacts, and tears down all resources + +#### Scenario: Policy rollout demo bypasses ControlCoordinator +- **WHEN** the LeRobot LIBERO policy rollout demo executes policy actions +- **THEN** actions flow from the robot policy module through the benchmark evaluation runner to the runtime sidecar as native runtime action frames without using ControlCoordinator, JointTrajectoryTask, EndEffectorDeltaTrajectoryTask, the SHM motor bridge, or motor action frames + +#### Scenario: Policy rollout demo enforces success gate +- **WHEN** the 50-episode policy rollout gate completes without setup or contract aborts +- **THEN** the demo passes only if the recorded success rate is greater than `0.50` + +#### Scenario: Policy rollout demo records required artifacts +- **WHEN** the LeRobot LIBERO policy rollout demo runs +- **THEN** it writes rollout summary, per-episode JSONL records, runtime description, contract description, checkpoint metadata, logs, and cleanup status to the artifact directory + +#### Scenario: Existing scripted demos remain unchanged +- **WHEN** the fake, Robosuite, or existing LIBERO-PRO motor demos are run +- **THEN** they continue to validate scripted runtime plumbing without requiring LeRobot policy dependencies or policy task success diff --git a/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/tasks.md b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/tasks.md new file mode 100644 index 0000000000..37e578ca0e --- /dev/null +++ b/openspec/changes/archive/2026-06-30-add-lerobot-libero-policy-rollout/tasks.md @@ -0,0 +1,61 @@ +## 1. Runtime Protocol + +- [x] 1.1 Add a discriminated `RuntimeActionFrame` model to `packages/dimos-runtime-protocol` with semantic `space_id`, numeric `values`, and sequence or tick identity. +- [x] 1.2 Update `StepRequest.action` to accept `MotorActionFrame | RuntimeActionFrame` while preserving existing motor-frame JSON compatibility. +- [x] 1.3 Add protocol validation tests for valid motor frames, valid runtime action frames, non-finite runtime action values, and ambiguous or unsupported action frame payloads. +- [x] 1.4 Update `RuntimeSidecarClient.step(...)` call sites only as needed for the new union model without changing existing motor demo behavior. + +## 2. Native LIBERO Sidecar Action Mode + +- [x] 2.1 Add explicit LIBERO action mode configuration to the LIBERO sidecar while keeping existing joint-position motor mode as the default for current demos. +- [x] 2.2 Implement native LIBERO action mode startup so it follows the official LeRobot LIBERO environment action setup and validates `env.action_spec` shape `(7,)` and bounds compatible with `[-1, 1]`. +- [x] 2.3 Extend runtime description metadata to advertise native action surface id, action shape, action bounds, action mode, task metadata, language, horizon, and camera configuration. +- [x] 2.4 Implement native-mode step handling for `RuntimeActionFrame(space_id="libero.ee_delta_6d_gripper.normalized.v1")` and reject `MotorActionFrame` in native mode with a clear protocol error. +- [x] 2.5 Preserve motor-mode validation so motor-frame mode still requires Panda joint-position plus gripper action compatibility. +- [x] 2.6 Add stubbed sidecar tests for native action mode description, action-spec validation, runtime-action stepping, motor-frame rejection in native mode, and unchanged motor-mode behavior. + +## 3. Robot Policy Module Core + +- [x] 3.1 Add shared rollout data models for backend-ready batches, backend output envelopes, backend descriptions, contract descriptions, runtime action outputs, rollout episode records, and rollout summaries. +- [x] 3.2 Define the batch-first `PolicyBackend` protocol with `initialize`, `reset_episode`, `infer_batch`, `close`, and `describe` methods. +- [x] 3.3 Implement `RobotPolicyModule` for public `reset(...)`, sample-to-action inference, contract conversion, backend inference, and runtime action emission without sidecar reset/step, scoring, success gate, or artifact ownership. +- [x] 3.4 Add unit tests using fake backend and fake contract objects to prove `RobotPolicyModule` calls the expected seams and resets backend episode state through its public reset method. + +## 4. Benchmark Policy Evaluation Runner + +- [x] 4.1 Implement `BenchmarkPolicyEvalRunner` orchestration for sidecar lifecycle, episode matrix, sidecar reset/step, policy module reset, sample construction, episode failure handling, metrics, artifacts, and cleanup. +- [x] 4.2 Add unit tests using fake runtime and fake robot policy module objects to prove the evaluation runner owns runtime reset/step, artifacts, success gate, and episode continuation/abort behavior. + +## 5. LeRobot Backend + +- [x] 5.1 Implement `LeRobotBackend` as an in-process `PolicyBackend` that loads `lerobot/VLA-JEPA-LIBERO`, initializes device/eval mode, and prepares required LeRobot processors. +- [x] 5.2 Implement `LeRobotBackend.reset_episode()` so LeRobot policy/action queue state is reset between benchmark episodes. +- [x] 5.3 Implement `LeRobotBackend.infer_batch(...)` under inference/no-grad mode and return a backend output envelope with inference metadata. +- [x] 5.4 Implement `LeRobotBackend.describe()` with checkpoint id, resolved checkpoint metadata when available, policy class when available, device, and episode reset support. +- [x] 5.5 Add import-boundary or optional-dependency tests so normal CI can import rollout modules without requiring LeRobot unless the backend is instantiated. +- [x] 5.6 Add a mocked LeRobot backend test covering initialization, episode reset, inference envelope shape, and metadata description without downloading real checkpoints. + +## 6. VLA-JEPA LIBERO Contract + +- [x] 6.1 Implement `VlaJepaLiberoRobotContract.to_backend_batch(...)` for hardcoded VLA-JEPA LIBERO observation mapping from sidecar agent-view camera, wrist or eye-in-hand camera, 8D robot state, and task language. +- [x] 6.2 Implement contract validation for missing observation streams, wrong image shape or dtype, wrong state vector size, and missing task language. +- [x] 6.3 Implement `VlaJepaLiberoRobotContract.from_backend_output(...)` to validate finite `(7,)` action output and produce `RuntimeActionFrame` with `space_id="libero.ee_delta_6d_gripper.normalized.v1"`. +- [x] 6.4 Implement `VlaJepaLiberoRobotContract.describe()` for artifact output, including expected observation streams, state shape, action space id, action shape, and action range. +- [x] 6.5 Add contract unit tests for successful batch conversion, input validation failures, successful runtime action conversion, and invalid backend output rejection. + +## 7. Policy Rollout Demo and Gate + +- [x] 7.1 Add a LeRobot LIBERO policy rollout demo entrypoint that starts the LIBERO sidecar in native action mode and constructs `BenchmarkPolicyEvalRunner(RuntimeSidecarClient, RobotPolicyModule(LeRobotBackend, VlaJepaLiberoRobotContract))`. +- [x] 7.2 Generate the 50-episode matrix from `libero_object` task indices `0..9` and init state indices `[0, 1, 2, 3, 4]` rather than requiring 50 config files. +- [x] 7.3 Write required artifacts: `summary.json`, `episodes.jsonl`, `runtime_description.json`, `contract_description.json`, `checkpoint_metadata.json`, logs, and cleanup status. +- [x] 7.4 Add optional `--save-videos` or equivalent video artifact support without saving full videos or image dumps by default. +- [x] 7.5 Enforce the manual/optional gate pass condition `success_rate > 0.50` after all 50 episodes complete without setup or contract aborts. +- [x] 7.6 Add a small fixed-action or fake-backend smoke path that exercises native runtime actions without downloading the real LeRobot checkpoint. + +## 8. Verification and Documentation + +- [x] 8.1 Run targeted protocol tests for runtime action frame validation and existing motor frame compatibility. +- [x] 8.2 Run targeted sidecar tests for native action mode and unchanged motor mode using stubbed backend dependencies. +- [x] 8.3 Run targeted robot policy module, benchmark evaluation runner, backend, and contract unit tests without real LIBERO or LeRobot downloads. +- [x] 8.4 Document how to run the optional real LeRobot LIBERO 50-episode gate, including dependency/assets expectations and expected artifacts. +- [x] 8.5 Verify the existing LIBERO-PRO motor demo path remains unchanged by the native action mode work. diff --git a/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/.openspec.yaml b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/.openspec.yaml new file mode 100644 index 0000000000..d6b53dee55 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-30 diff --git a/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/design.md b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/design.md new file mode 100644 index 0000000000..ff21ea77ed --- /dev/null +++ b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/design.md @@ -0,0 +1,104 @@ +## Context + +The archived LeRobot LIBERO rollout change added a successful policy benchmark path, but the current `RobotPolicyModule` is a plain Python class constructed directly by the rollout script. That helped move quickly, but it conflicts with DimOS naming and composition expectations: a DimOS module is a `dimos.core.module.Module` with blueprint construction, RPC lifecycle, and worker-process deployment semantics. + +The current benchmark also passes a benchmark-specific `RuntimeObservationSample` into policy inference and receives a `RuntimeActionFrame` back. That makes the policy component harder to reuse for future simulator and real-robot rollout, where observations will come from DimOS streams or temporal assemblers and actions may go to ControlCoordinator or another action-surface executor rather than a runtime sidecar. + +## Goals / Non-Goals + +**Goals:** + +- Make `RobotPolicyModule` a real DimOS `Module` configured through blueprint kwargs. +- Select policy backends and robot policy contracts through lazy registries, following the control task registry pattern. +- Define `RobotLearningSample` as the reusable public policy input boundary. +- Define `RobotPolicyAction` as the reusable public policy output boundary. +- Keep the LIBERO benchmark lockstep and sidecar-native, but have benchmark evaluation call DimOS module RPCs rather than plain service methods. +- Preserve the existing 50-episode LeRobot LIBERO gate semantics and artifact expectations. + +**Non-Goals:** + +- Do not add ControlCoordinator action-surface execution in this change. +- Do not introduce streaming policy inference for v1 evaluation. +- Do not change the official `lerobot/VLA-JEPA-LIBERO` checkpoint, suite, episode matrix, or `success_rate > 0.50` gate. +- Do not add a new optional dependency beyond the existing LeRobot/LIBERO rollout dependencies. + +## Decisions + +### 1. Convert `RobotPolicyModule` in place + +`RobotPolicyModule` SHALL become a subclass of `dimos.core.module.Module` rather than being renamed to a separate engine class. + +Rationale: the user-facing name should match the DimOS concept. Adding a second engine class would preserve testability, but it would also preserve ambiguity around which object owns policy lifecycle. The module can still keep private helper methods for unit-testable behavior. + +### 2. Use lazy registries for backend and contract selection + +The module config selects `backend_type`, `backend_params`, `contract_type`, and `contract_params`. Registries map those names to lazy factory import paths. + +This follows the control task registry style rather than the eager hardware adapter registry style because policy backends may import heavy or optional libraries such as LeRobot, torch, and checkpoint-specific dependencies. + +Conceptual config: + +```python +class RobotPolicyModuleConfig(ModuleConfig): + backend_type: str = "lerobot" + backend_params: dict[str, object] = {...} + contract_type: str = "vla_jepa_libero" + contract_params: dict[str, object] = {} +``` + +### 3. Use RPC-only policy inference for v1 evaluation + +For the benchmark path, `BenchmarkPolicyEvalModule` calls `RobotPolicyModule.infer_action(sample)` by RPC. Streams can be added later for online rollout, but v1 evaluation is lockstep: + +```text +observation -> infer one action -> step environment -> next observation +``` + +RPC keeps timeout/error handling simple and avoids correlation IDs, action queues, and stale-action backpressure semantics. + +### 4. Replace benchmark-shaped sample with `RobotLearningSample` + +`RobotPolicyModule` consumes a reusable `RobotLearningSample`, not `RuntimeObservationSample`. The sample is role-keyed and runtime-independent. Producers may include benchmark evaluation modules, future temporal sample assemblers, replay loaders, or other simulator adapters. + +The LIBERO benchmark module remains responsible for converting runtime sidecar observations and payloads into `RobotLearningSample` for v1. + +### 5. Replace runtime frame output with `RobotPolicyAction` + +`RobotPolicyModule` returns a runtime-independent `RobotPolicyAction`, not `RuntimeActionFrame`. Benchmark evaluation adapts `RobotPolicyAction` into `RuntimeActionFrame` before calling the sidecar. Future real-robot execution can adapt the same policy action into a ControlCoordinator action-surface command or other execution artifact. + +### 6. Add module-backed benchmark evaluation while preserving script convenience + +The script remains a developer entrypoint, but it should construct and run a module-backed evaluation composition. The DimOS-shaped flow is: + +```text +BenchmarkPolicyEvalModule + -> LiberoRuntimeModule.reset/step + -> builds RobotLearningSample + -> RobotPolicyModule.infer_action(sample) + -> adapts RobotPolicyAction to RuntimeActionFrame + -> LiberoRuntimeModule.step(action) +``` + +This keeps benchmark lifecycle outside the policy module while making policy inference a real module participant. + +## Risks / Trade-offs + +- **Registry indirection can hide errors until runtime** → fail fast with clear unknown-type messages and expose available registry keys. +- **RPC-only inference is less future-real-time than streams** → explicitly scope streams to later online rollout; keep method names and data models stream-compatible. +- **`RobotLearningSample` can become too generic** → keep it role-keyed but narrow for v1, and let robot policy contracts validate required roles. +- **Script convenience may still look non-blueprint-like** → ensure the script builds the same modules/config that a blueprint would use, even if it runs them locally for benchmark convenience. + +## Migration Plan + +1. Introduce reusable `RobotLearningSample` and `RobotPolicyAction` models. +2. Convert `RobotPolicyModule` into a DimOS `Module` with config and RPC methods. +3. Add backend and contract registries plus default LeRobot/VLA-JEPA LIBERO registrations. +4. Update benchmark evaluation to build `RobotLearningSample` internally and adapt `RobotPolicyAction` to `RuntimeActionFrame`. +5. Add a module-backed benchmark evaluation module or equivalent blueprint-compatible module composition. +6. Update the rollout script to use the module-backed flow while preserving CLI behavior and artifacts. +7. Update tests and docs, then rerun OpenSpec and targeted validation. + +## Open Questions + +- Whether the module-backed benchmark runner should run inside a deployed blueprint for every test, or expose a local harness that instantiates modules directly while preserving the same config/RPC boundaries. +- Whether the first stream-based policy interface should be added in this change as disabled/non-used scaffolding, or deferred entirely until online rollout work. diff --git a/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/proposal.md b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/proposal.md new file mode 100644 index 0000000000..0c8c0fdd89 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/proposal.md @@ -0,0 +1,34 @@ +## Why + +The first LeRobot LIBERO rollout implementation proves policy inference and benchmark success, but its core `RobotPolicyModule` is a plain Python service class rather than a first-class DimOS `Module`. That makes the rollout path harder to compose in blueprints and blurs the reusable boundary needed for later simulator and real-world policy rollout. + +This follow-up aligns the policy rollout stack with normal DimOS workflow: modules are configured through blueprints, heavy policy pieces are selected through registries, policy inputs use runtime-independent robot-learning samples, and policy outputs are runtime-independent policy actions. + +## What Changes + +- Convert the existing robot policy inference component into a real DimOS `Module` with RPC lifecycle and inference methods. +- Add lazy registries for policy backends and robot policy contracts, following the control task registry pattern. +- Replace benchmark-shaped policy input with a reusable `RobotLearningSample` boundary. +- Replace runtime protocol action output from the policy module with a reusable `RobotPolicyAction` boundary. +- Move LIBERO-specific conversion from runtime observations to `RobotLearningSample` into benchmark evaluation code as an explicit named sample-building seam. +- Add module-backed benchmark evaluation flow that can be launched by blueprint composition while preserving the existing 50-episode LeRobot LIBERO gate semantics. +- Preserve the script-based policy gate as a convenience entrypoint, but have it construct/run the DimOS module-backed workflow rather than directly wiring plain service objects. + +## Capabilities + +### New Capabilities + +None. + +### Modified Capabilities + +- `robot-policy-module`: make the policy module a first-class DimOS module, add registry-based backend/contract selection, define `RobotLearningSample` input and `RobotPolicyAction` output boundaries. +- `benchmark-policy-evaluation`: make benchmark evaluation use DimOS modules/blueprints for policy inference while retaining ownership of episode lifecycle, runtime stepping, scoring, artifacts, and gates. +- `runtime-scripted-demos`: update the LeRobot LIBERO demo requirement so the script exercises the module-backed evaluation path instead of a plain Python service stack. + +## Impact + +- Affected code: `dimos/robot_learning/policy_rollout/`, LeRobot LIBERO rollout script, benchmark policy evaluation tests, and OpenSpec specs. +- API impact: `RobotPolicyModule` construction becomes config/registry based; public inference consumes `RobotLearningSample` and returns `RobotPolicyAction` rather than benchmark-specific samples or `RuntimeActionFrame`. +- Runtime impact: benchmark execution remains lockstep and sidecar-native for v1; no ControlCoordinator action-surface integration is added in this change. +- Dependency impact: no new third-party dependency is expected; optional LeRobot/LIBERO dependency boundaries remain unchanged. diff --git a/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/benchmark-policy-evaluation/spec.md b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/benchmark-policy-evaluation/spec.md new file mode 100644 index 0000000000..fbc1da7826 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/benchmark-policy-evaluation/spec.md @@ -0,0 +1,36 @@ +## ADDED Requirements + +### Requirement: Benchmark sample builder +The system SHALL provide an explicit LIBERO benchmark sample-building seam that converts runtime sidecar observations and payloads into robot learning samples. + +#### Scenario: Evaluation builds sample from runtime observations +- **WHEN** benchmark evaluation receives LIBERO runtime observations and payload references for an episode tick +- **THEN** it resolves the required payloads and produces a robot learning sample with policy-facing observation roles, task context, timestamps, and metadata + +#### Scenario: Sample builder remains outside policy module +- **WHEN** LIBERO runtime observations must be converted to robot learning samples +- **THEN** the conversion is owned by benchmark evaluation or its sample-builder seam rather than by the robot policy module + +### Requirement: Benchmark action adapter +The system SHALL adapt robot policy actions into native runtime action frames inside benchmark evaluation before stepping the runtime sidecar. + +#### Scenario: Evaluation adapts policy action to runtime frame +- **WHEN** the robot policy module returns a robot policy action for the LIBERO native action surface +- **THEN** benchmark evaluation converts it into a runtime action frame with the matching action-space identity before calling the runtime sidecar step method + +## MODIFIED Requirements + +### Requirement: Benchmark policy evaluation runner +The system SHALL provide a module-backed benchmark policy evaluation runner that owns episode matrix selection, runtime sidecar lifecycle, runtime reset and step calls, scoring, success gates, metrics, artifacts, and cleanup for policy-driven benchmark rollouts. + +#### Scenario: Evaluation runner uses robot policy module for actions +- **WHEN** the evaluation runner has a current sidecar observation during an episode +- **THEN** it builds a robot learning sample, sends the sample to the robot policy module through its module-facing inference API, adapts the returned robot policy action to a runtime action frame, and uses that frame for the sidecar step call + +#### Scenario: Evaluation runner owns episode lifecycle +- **WHEN** the evaluation runner starts an episode +- **THEN** it resets the runtime sidecar, calls the robot policy module public reset method, steps the runtime with adapted policy actions, records episode metrics, and applies the success gate outside the robot policy module + +#### Scenario: Evaluation runner can be launched through DimOS module composition +- **WHEN** a developer launches the LeRobot LIBERO policy evaluation path +- **THEN** benchmark evaluation, policy inference, and runtime sidecar access are represented as DimOS modules or blueprint-compatible module configuration rather than only as directly wired plain Python service objects diff --git a/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/robot-policy-module/spec.md b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/robot-policy-module/spec.md new file mode 100644 index 0000000000..d3b14f3ab0 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/robot-policy-module/spec.md @@ -0,0 +1,69 @@ +## ADDED Requirements + +### Requirement: Policy backend registry +The system SHALL provide a lazy policy backend registry that maps backend type names to importable backend factory paths. + +#### Scenario: Module creates backend from registry +- **WHEN** a robot policy module is configured with a registered backend type and backend parameters +- **THEN** it resolves the backend factory lazily and constructs the policy backend from configuration rather than requiring a live backend object in the blueprint + +#### Scenario: Unknown backend type fails clearly +- **WHEN** a robot policy module is configured with an unknown backend type +- **THEN** backend construction fails with a message that includes the unknown type and the available backend types + +### Requirement: Robot policy contract registry +The system SHALL provide a lazy robot policy contract registry that maps contract type names to importable contract factory paths. + +#### Scenario: Module creates contract from registry +- **WHEN** a robot policy module is configured with a registered contract type and contract parameters +- **THEN** it resolves the contract factory lazily and constructs the robot policy contract from configuration rather than requiring a live contract object in the blueprint + +#### Scenario: Unknown contract type fails clearly +- **WHEN** a robot policy module is configured with an unknown contract type +- **THEN** contract construction fails with a message that includes the unknown type and the available contract types + +### Requirement: Robot learning sample boundary +The system SHALL define a reusable robot learning sample model for policy inference inputs that is not tied to a benchmark sidecar response type. + +#### Scenario: Policy module accepts runtime-independent samples +- **WHEN** a robot policy module receives an inference request +- **THEN** the request carries a robot learning sample with semantically named observation roles, task context, timestamps, and metadata rather than a benchmark-specific runtime observation sample + +### Requirement: Robot policy action boundary +The system SHALL define a reusable robot policy action model for policy inference outputs before adaptation to runtime frames or real robot control commands. + +#### Scenario: Policy module returns runtime-independent action +- **WHEN** a robot policy module completes inference and contract conversion +- **THEN** it returns a robot policy action with action-space identity, numeric values, and metadata rather than a runtime sidecar action frame + +## MODIFIED Requirements + +### Requirement: Robot policy module inference boundary +The system SHALL provide a robot policy module that is a first-class DimOS `Module` and owns policy backend lifecycle, public policy reset, robot policy contract conversion, backend inference, and robot policy action emission without owning benchmark episode lifecycle, runtime reset or step calls, scoring, success gates, or artifact writing. + +#### Scenario: Robot policy module emits one action through the architecture seams +- **WHEN** the robot policy module receives a ready robot learning sample and has an initialized policy backend +- **THEN** it converts the sample through the robot policy contract, invokes the backend through `infer_batch`, converts the backend output back through the contract, and returns or emits the resulting robot policy action + +#### Scenario: Robot policy module exposes public reset +- **WHEN** a benchmark runner starts a new policy episode +- **THEN** it can call the robot policy module public reset method, and the robot policy module resets backend episode state before the next inference + +#### Scenario: Robot policy module is blueprint-configured +- **WHEN** a blueprint constructs the robot policy module +- **THEN** the blueprint supplies backend and contract types and parameters as module configuration rather than passing live backend or contract objects + +### Requirement: VLA-JEPA LIBERO robot policy contract +The system SHALL provide a narrow robot policy contract for the VLA-JEPA LIBERO rollout that converts robot learning samples into LeRobot backend batches and converts LeRobot backend output into robot policy actions. + +#### Scenario: Contract maps robot learning sample to LeRobot batch +- **WHEN** the contract receives a ready robot learning sample for VLA-JEPA LIBERO rollout +- **THEN** it maps agent-view camera data to `observation.images.image`, wrist or eye-in-hand camera data to `observation.images.image2`, the 8D robot state to `observation.state`, and task language to the backend prompt field expected by the backend path + +#### Scenario: Contract rejects semantic input mismatch +- **WHEN** a supposedly ready sample is missing a required role or has incompatible image, state, dtype, or shape semantics +- **THEN** the contract raises a contract conversion failure before backend inference runs + +#### Scenario: Contract converts backend output to robot policy action +- **WHEN** the contract receives a backend output envelope containing a valid VLA-JEPA LIBERO action +- **THEN** it returns a robot policy action with action-space id `libero.ee_delta_6d_gripper.normalized.v1` and finite `float32[7]` values compatible with the native LIBERO action range diff --git a/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/runtime-scripted-demos/spec.md b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/runtime-scripted-demos/spec.md new file mode 100644 index 0000000000..e1bc940fd4 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/specs/runtime-scripted-demos/spec.md @@ -0,0 +1,24 @@ +## MODIFIED Requirements + +### Requirement: LeRobot LIBERO policy rollout demo +The system SHALL include a script-based or module-backed LeRobot LIBERO policy rollout demo that validates policy loading, contract conversion, native runtime actions, sidecar stepping, score collection, artifact output, and teardown through the same module-backed policy evaluation path used by DimOS workflow. + +#### Scenario: Policy rollout demo starts native sidecar and policy rollout stack +- **WHEN** a developer runs the LeRobot LIBERO policy rollout demo with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the demo starts the LIBERO sidecar in native LIBERO action mode, initializes module-backed benchmark evaluation with a robot policy module, LeRobot backend, and VLA-JEPA LIBERO contract, runs the configured episode matrix, writes artifacts, and tears down all resources + +#### Scenario: Policy rollout demo bypasses ControlCoordinator +- **WHEN** the LeRobot LIBERO policy rollout demo executes policy actions +- **THEN** actions flow from the robot policy module through benchmark evaluation to the runtime sidecar as native runtime action frames without using ControlCoordinator, JointTrajectoryTask, EndEffectorDeltaTrajectoryTask, the SHM motor bridge, or motor action frames + +#### Scenario: Policy rollout demo enforces success gate +- **WHEN** the 50-episode policy rollout gate completes without setup or contract aborts +- **THEN** the demo passes only if the recorded success rate is greater than `0.50` + +#### Scenario: Policy rollout demo records required artifacts +- **WHEN** the LeRobot LIBERO policy rollout demo runs +- **THEN** it writes rollout summary, per-episode JSONL records, runtime description, contract description, checkpoint metadata, logs, and cleanup status to the artifact directory + +#### Scenario: Existing scripted demos remain unchanged +- **WHEN** the fake, Robosuite, or existing LIBERO-PRO motor demos are run +- **THEN** they continue to validate scripted runtime plumbing without requiring LeRobot policy dependencies or policy task success diff --git a/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/tasks.md b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/tasks.md new file mode 100644 index 0000000000..c590b9db39 --- /dev/null +++ b/openspec/changes/archive/2026-06-30-moduleize-robot-policy-rollout/tasks.md @@ -0,0 +1,51 @@ +## 1. Data Model Boundaries + +- [x] 1.1 Add `RobotLearningSample` and related observation-role models for runtime-independent policy inference input. +- [x] 1.2 Add `RobotPolicyAction` for runtime-independent policy inference output. +- [x] 1.3 Update existing model exports and tests to cover sample/action serialization and validation. + +## 2. Backend and Contract Registries + +- [x] 2.1 Add lazy policy backend registry modeled after `ControlTaskRegistry`. +- [x] 2.2 Add lazy robot policy contract registry modeled after `ControlTaskRegistry`. +- [x] 2.3 Register the LeRobot backend under `lerobot` without importing LeRobot at registry discovery time. +- [x] 2.4 Register the VLA-JEPA LIBERO contract under `vla_jepa_libero`. +- [x] 2.5 Add registry tests for successful creation, lazy import behavior, duplicate detection, and unknown type errors. + +## 3. RobotPolicyModule as DimOS Module + +- [x] 3.1 Convert `RobotPolicyModule` to subclass `dimos.core.module.Module` with a `RobotPolicyModuleConfig`. +- [x] 3.2 Configure backend and contract through `backend_type`, `backend_params`, `contract_type`, and `contract_params`. +- [x] 3.3 Expose RPC methods for policy reset, inference, backend description, and contract description. +- [x] 3.4 Ensure inference accepts `RobotLearningSample` and returns `RobotPolicyAction`. +- [x] 3.5 Preserve backend lifecycle behavior: lazy or start-time initialization, episode reset, close/stop cleanup. +- [x] 3.6 Update existing RobotPolicyModule tests for DimOS module construction and RPC-callable methods. + +## 4. Contract and Backend Adaptation + +- [x] 4.1 Update `VlaJepaLiberoRobotContract` to consume `RobotLearningSample` instead of benchmark-specific runtime samples. +- [x] 4.2 Update `VlaJepaLiberoRobotContract` to return `RobotPolicyAction` instead of runtime action output/frame types. +- [x] 4.3 Keep `LeRobotBackend` batch-first and registry-created while preserving optional dependency behavior. +- [x] 4.4 Update contract/backend tests for the new sample and action models. + +## 5. Module-Backed Benchmark Evaluation + +- [x] 5.1 Add or refactor benchmark evaluation into a DimOS module-compatible component that owns episode lifecycle, scoring, artifacts, and cleanup. +- [x] 5.2 Add an explicit LIBERO sample-building seam that converts sidecar observations, payloads, task context, and metadata into `RobotLearningSample`. +- [x] 5.3 Adapt `RobotPolicyAction` to `RuntimeActionFrame` inside benchmark evaluation before sidecar stepping. +- [x] 5.4 Ensure the policy module no longer depends on benchmark-specific sample classes or runtime protocol action frames. +- [x] 5.5 Add tests proving benchmark evaluation builds samples, calls the policy module, adapts actions, and continues/aborts according to existing gate rules. + +## 6. Blueprint and Demo Integration + +- [x] 6.1 Add a blueprint or blueprint-compatible factory for the module-backed LeRobot LIBERO evaluation path. +- [x] 6.2 Update `demo_lerobot_libero_policy_rollout.py` to construct/run the module-backed workflow while preserving CLI behavior, artifacts, videos, fake-backend smoke path, and gate enforcement. +- [x] 6.3 Ensure existing fake, Robosuite, and LIBERO-PRO motor demos remain unchanged. + +## 7. Verification and Documentation + +- [x] 7.1 Update rollout documentation to describe the DimOS module workflow, registries, `RobotLearningSample`, and `RobotPolicyAction` boundaries. +- [x] 7.2 Run targeted unit tests for model, registry, RobotPolicyModule, contract, backend, and benchmark evaluation changes. +- [x] 7.3 Run Ruff on changed rollout, sidecar, protocol, and test files. +- [x] 7.4 Run `openspec validate moduleize-robot-policy-rollout --type change`. +- [x] 7.5 Confirm the existing 50-episode benchmark gate command and artifact expectations remain unchanged. diff --git a/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/.openspec.yaml b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/.openspec.yaml new file mode 100644 index 0000000000..e7cc357ca0 --- /dev/null +++ b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-01 diff --git a/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/design.md b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/design.md new file mode 100644 index 0000000000..d7a38ec3b3 --- /dev/null +++ b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/design.md @@ -0,0 +1,55 @@ +## Context + +The policy rollout code introduced a reusable robot policy module, backend registry, LeRobot VLA-JEPA backend, and LIBERO benchmark evaluation runner. Review feedback identified that several seams are still shaped by the first benchmark integration rather than by a narrow policy-module contract: policy inputs carry episode/task-index bookkeeping, backend output accepts arbitrary objects, contract descriptions are produced but not consumed, and the LeRobot backend uses dynamic imports and local Protocols instead of the official LeRobot API surface. + +The cleanup must be small enough to land as PR review follow-up. It should not redesign high-throughput image transport or stream sample assembly. + +## Goals / Non-Goals + +**Goals:** + +- Make the policy input boundary observation-focused and free of benchmark lifecycle fields. +- Keep policy language prompts available as contract-specific metadata or observation data, not as a generic top-level `task` field. +- Return a constrained flat numeric backend action tuple before contract conversion. +- Remove unused contract description APIs and artifacts. +- Place backend protocol and LeRobot implementation under the backend package layout expected by review. +- Simplify LeRobot VLA-JEPA loading and processor setup around official top-level imports and factory helpers. + +**Non-Goals:** + +- No redesign of image transport, LCM payload shape, SHM streaming, or asynchronous sample assembly. +- No new policy families beyond the existing VLA-JEPA LIBERO path. +- No change to benchmark episode matrix, success gate, or runtime sidecar protocol. + +## Decisions + +1. **Rename the input model to `RobotPolicyObservation`.** + - Rationale: the module consumes one policy observation at an inference step; the previous `RobotLearningSample` name was broad and encouraged carrying unrelated IDs. + - Alternative considered: keep `RobotLearningSample` and delete fields. Rejected because the name still suggests a training/sample artifact rather than a policy inference observation. + +2. **Remove benchmark identifiers from policy observations.** + - Rationale: `episode_id`, `task_id`, task/init indices, tick counters, and synthetic sample IDs belong to evaluation requests/records. The policy module should only see observation roles, timestamps, and policy-relevant metadata. + - Alternative considered: keep the fields as optional metadata for debugging. Rejected because it preserves the coupling called out by review. + +3. **Carry language prompt through metadata/observations.** + - Rationale: language is a policy/contract input for VLA-JEPA, but not a universal base-model field. The VLA-JEPA contract can read `metadata["language"]` or an observation role and raise a clear error when missing. + - Alternative considered: keep top-level `task`. Rejected because it implies every robot policy observation has a generic task string. + +4. **Normalize backend output to `tuple[float, ...]`.** + - Rationale: the only backend output consumed by the contract is a numeric action vector. Normalizing tensors/arrays/lists at the backend boundary removes arbitrary `object` output and simplifies validation. + - Alternative considered: generic typed envelopes. Rejected as unnecessary complexity for the current single action-vector path. + +5. **Remove contract description APIs and artifacts.** + - Rationale: contract descriptions are currently only written/tested, not used to drive behavior. Action-space validation remains in the contract itself. + - Alternative considered: keep descriptions for documentation. Rejected because review requested removal until there is a real consumer. + +6. **Use official LeRobot imports in the backend module.** + - Rationale: current LeRobot exports `VLAJEPAPolicy` and processor helpers through top-level package APIs. Direct imports remove local importlib/protocol indirection and surface optional dependency failures at backend module load time, which remains lazy through the registry. + - Alternative considered: keep dynamic imports for optional dependency isolation. Rejected because the registry already lazy-loads backend modules and review specifically requested top-level imports unless a concrete issue appears. + +## Risks / Trade-offs + +- **Breaking internal API rename** → Update all policy rollout tests, registry paths, benchmark runner code, docs, and specs in the same change. +- **Optional LeRobot import errors may occur earlier within backend module import** → Backend modules remain lazy-loaded from the registry, so environments that do not instantiate `lerobot` backend are unaffected. +- **Removing contract descriptions reduces artifact metadata** → Keep backend metadata and runtime/episode artifacts; reintroduce contract descriptions later only when there is a consumer. +- **Skipping image transport design leaves a review concern open** → Reply separately that transport/performance is intentionally deferred to a dedicated design. diff --git a/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/proposal.md b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/proposal.md new file mode 100644 index 0000000000..205f096f01 --- /dev/null +++ b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/proposal.md @@ -0,0 +1,29 @@ +## Why + +PR review identified that the policy rollout module boundary still mixes policy inputs with benchmark bookkeeping and carries unused contract-description plumbing. Cleaning this up now keeps the new module API narrow before downstream code starts depending on the first rollout implementation. + +## What Changes + +- **BREAKING** Rename the policy input boundary from `RobotLearningSample` to an observation-focused model and remove unused or benchmark-specific fields, including `sample_id`, `episode_id`, `tick_id`, `task`, `task_id`, `task_index`, and `init_state_index`. +- Preserve policy language prompts as contract-specific observation metadata rather than a top-level generic `task` field. +- **BREAKING** Constrain backend output envelopes to flat numeric action tuples instead of arbitrary `object` output. +- Remove the unused robot policy contract description API and stop writing `contract_description.json` artifacts. +- Move backend interfaces/implementations into the backend package layout and simplify the LeRobot VLA-JEPA backend to use official top-level LeRobot imports and processor APIs. +- Keep image transport and large-observation streaming design out of scope for this cleanup; that will be handled separately. + +## Capabilities + +### New Capabilities + +None. + +### Modified Capabilities + +- `robot-policy-module`: Narrow the policy input/output contract, backend package layout, and LeRobot backend loading behavior. +- `benchmark-policy-evaluation`: Update benchmark evaluation artifacts and sample-building expectations after policy inputs stop carrying benchmark lifecycle fields. + +## Impact + +- Affected code: `dimos/robot_learning/policy_rollout/`, `scripts/benchmarks/demo_lerobot_libero_policy_rollout.py`, and rollout documentation/tests. +- API impact: policy input model and backend output envelope types change; contract description APIs and artifact output are removed. +- Dependency impact: no new dependencies; LeRobot optional backend imports follow the official `lerobot.policies` API when the backend module is loaded. diff --git a/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/specs/benchmark-policy-evaluation/spec.md b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/specs/benchmark-policy-evaluation/spec.md new file mode 100644 index 0000000000..cc1f9a841a --- /dev/null +++ b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/specs/benchmark-policy-evaluation/spec.md @@ -0,0 +1,42 @@ +## MODIFIED Requirements + +### Requirement: Benchmark policy evaluation runner +The system SHALL provide a module-backed benchmark policy evaluation runner that owns episode matrix selection, runtime sidecar lifecycle, runtime reset and step calls, scoring, success gates, metrics, artifacts, and cleanup for policy-driven benchmark rollouts. + +#### Scenario: Evaluation runner uses robot policy module for actions +- **WHEN** the evaluation runner has a current sidecar observation during an episode +- **THEN** it builds a robot policy observation, sends the observation to the robot policy module through its module-facing inference API, adapts the returned robot policy action to a runtime action frame, and uses that frame for the sidecar step call + +#### Scenario: Evaluation runner owns episode lifecycle +- **WHEN** the evaluation runner starts an episode +- **THEN** it resets the runtime sidecar, calls the robot policy module public reset method, steps the runtime with adapted policy actions, records episode metrics, and applies the success gate outside the robot policy module + +#### Scenario: Evaluation runner can be launched through DimOS module composition +- **WHEN** a developer launches the LeRobot LIBERO policy evaluation path +- **THEN** benchmark evaluation, policy inference, and runtime sidecar access are represented as DimOS modules or blueprint-compatible module configuration rather than only as directly wired plain Python service objects + +### Requirement: Rollout artifacts +The system SHALL write structured rollout artifacts that make policy, runtime, and episode behavior inspectable without storing full videos or image dumps by default. + +#### Scenario: Required metadata artifacts are written +- **WHEN** a policy rollout gate starts or completes +- **THEN** it writes `summary.json`, `episodes.jsonl`, `runtime_description.json`, and `checkpoint_metadata.json` in the artifact directory + +#### Scenario: Episode records include rollout diagnostics +- **WHEN** an episode record is written +- **THEN** it includes task index, init state index, episode id, success, step count, reward sum, done state, failure reason when present, action shape, action min/max, and observed stream names + +#### Scenario: Videos are opt-in +- **WHEN** the rollout is run without a video-saving option +- **THEN** it does not write full rollout videos or complete image dumps by default + +### Requirement: Benchmark sample builder +The system SHALL provide an explicit LIBERO benchmark sample-building seam that converts runtime sidecar observations and payloads into robot policy observations. + +#### Scenario: Evaluation builds observation from runtime observations +- **WHEN** benchmark evaluation receives LIBERO runtime observations and payload references for an episode tick +- **THEN** it resolves the required payloads and produces a robot policy observation with policy-facing observation roles, timestamps, and metadata while keeping benchmark episode identifiers in evaluation-layer records and requests + +#### Scenario: Sample builder remains outside policy module +- **WHEN** LIBERO runtime observations must be converted to robot policy observations +- **THEN** the conversion is owned by benchmark evaluation or its sample-builder seam rather than by the robot policy module diff --git a/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/specs/robot-policy-module/spec.md b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/specs/robot-policy-module/spec.md new file mode 100644 index 0000000000..cb88a6d9eb --- /dev/null +++ b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/specs/robot-policy-module/spec.md @@ -0,0 +1,78 @@ +## MODIFIED Requirements + +### Requirement: Robot policy module inference boundary +The system SHALL provide a robot policy module that is a first-class DimOS `Module` and owns policy backend lifecycle, public policy reset, robot policy contract conversion, backend inference, and robot policy action emission without owning benchmark episode lifecycle, runtime reset or step calls, scoring, success gates, or artifact writing. + +#### Scenario: Robot policy module emits one action through the architecture seams +- **WHEN** the robot policy module receives a ready robot policy observation and has an initialized policy backend +- **THEN** it converts the observation through the robot policy contract, invokes the backend through `infer_batch`, converts the backend output back through the contract, and returns or emits the resulting robot policy action + +#### Scenario: Robot policy module exposes public reset +- **WHEN** a benchmark runner starts a new policy episode +- **THEN** it can call the robot policy module public reset method, and the robot policy module resets backend episode state before the next inference + +#### Scenario: Robot policy module is blueprint-configured +- **WHEN** a blueprint constructs the robot policy module +- **THEN** the blueprint supplies backend and contract types and parameters as module configuration rather than passing live backend or contract objects + +### Requirement: Batch-first policy backend +The system SHALL define a policy backend interface whose primary inference method accepts backend-ready batches and returns backend output envelopes containing flat numeric action tuples and inference metadata. + +#### Scenario: Robot policy module does not depend on LeRobot select_action +- **WHEN** the robot policy module needs a policy action +- **THEN** it calls the backend batch inference method rather than depending on LeRobot-specific `select_action` or `predict_action_chunk` APIs + +#### Scenario: Backend describes loaded policy metadata +- **WHEN** a policy backend is initialized +- **THEN** it can describe backend type, checkpoint identifier, resolved checkpoint metadata when available, device, policy class when available, and episode reset support for rollout artifacts + +#### Scenario: Backend output is constrained +- **WHEN** a policy backend completes inference +- **THEN** it returns a backend output envelope whose output is a flat tuple of finite numeric action values suitable for contract validation + +### Requirement: LeRobot backend for VLA-JEPA LIBERO +The system SHALL provide an in-process LeRobot backend capable of loading and running the official `lerobot/VLA-JEPA-LIBERO` checkpoint for LIBERO policy rollout using official LeRobot policy and processor APIs. + +#### Scenario: Backend initializes official checkpoint +- **WHEN** the LeRobot backend is configured with `lerobot/VLA-JEPA-LIBERO` +- **THEN** it loads the policy through the official VLA-JEPA policy API, prepares required LeRobot preprocessing and postprocessing through official processor factories, moves the policy to the configured device, and enters inference/evaluation mode + +#### Scenario: Backend returns an output envelope +- **WHEN** the LeRobot backend completes inference for a backend-ready batch +- **THEN** it returns a backend output envelope containing a flat action tuple and inference metadata needed for validation and artifacts + +### Requirement: VLA-JEPA LIBERO robot policy contract +The system SHALL provide a narrow robot policy contract for the VLA-JEPA LIBERO rollout that converts robot policy observations into LeRobot backend batches and converts LeRobot backend output into robot policy actions. + +#### Scenario: Contract maps robot policy observation to LeRobot batch +- **WHEN** the contract receives a ready robot policy observation for VLA-JEPA LIBERO rollout +- **THEN** it maps agent-view camera data to `observation.images.image`, wrist or eye-in-hand camera data to `observation.images.image2`, the 8D robot state to `observation.state`, and language metadata or observation data to the backend prompt field expected by the backend path + +#### Scenario: Contract rejects semantic input mismatch +- **WHEN** a supposedly ready observation is missing a required role or has incompatible image, state, dtype, shape, or language semantics +- **THEN** the contract raises a contract conversion failure before backend inference runs + +#### Scenario: Contract converts backend output to robot policy action +- **WHEN** the contract receives a backend output envelope containing a valid VLA-JEPA LIBERO action tuple +- **THEN** it returns a robot policy action with action-space id `libero.ee_delta_6d_gripper.normalized.v1` and finite `float32[7]` values compatible with the native LIBERO action range + +### Requirement: Robot learning sample boundary +The system SHALL define a reusable robot policy observation model for policy inference inputs that is not tied to a benchmark sidecar response type or benchmark episode lifecycle identifiers. + +#### Scenario: Policy module accepts runtime-independent observations +- **WHEN** a robot policy module receives an inference request +- **THEN** the request carries a robot policy observation with semantically named observation roles, timestamps, and metadata rather than a benchmark-specific runtime observation sample or benchmark episode identifiers + +#### Scenario: Language remains contract-specific input data +- **WHEN** a policy contract requires a language prompt +- **THEN** it reads the prompt from observation metadata or observation roles rather than from a generic top-level task field on every policy observation + +## REMOVED Requirements + +### Requirement: Robot policy contract descriptions +**Reason**: Contract descriptions were only written as artifacts and tested; no runtime component consumes them. +**Migration**: Keep semantic validation in contract conversion code and reintroduce a description API later only when a consumer requires it. + +#### Scenario: Contract description artifact is not required +- **WHEN** policy rollout artifacts are written +- **THEN** the system does not require a robot policy contract description artifact to inspect or validate the run diff --git a/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/tasks.md b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/tasks.md new file mode 100644 index 0000000000..37e1a57cca --- /dev/null +++ b/openspec/changes/archive/2026-07-01-simplify-robot-policy-observation/tasks.md @@ -0,0 +1,27 @@ +## 1. Backend layout and LeRobot API cleanup + +- [x] 1.1 Move the generic policy backend Protocol into the backend package layout and update imports. +- [x] 1.2 Move the LeRobot backend implementation under `backends/lerobot/` and update registry paths. +- [x] 1.3 Remove newly-added namespace `__init__.py` files that violate repository conventions. +- [x] 1.4 Replace dynamic LeRobot policy/processor imports and local typing Protocols with official LeRobot API imports. +- [x] 1.5 Simplify LeRobot processor setup so processors are always prepared through the official factory path. + +## 2. Policy observation and backend output model cleanup + +- [x] 2.1 Rename `RobotLearningSample` to `RobotPolicyObservation` and remove benchmark-specific fields plus the top-level `task` field. +- [x] 2.2 Keep language prompts in observation metadata or observation roles and update the VLA-JEPA contract to read them there. +- [x] 2.3 Change `BackendOutputEnvelope.output` to `tuple[float, ...]` and normalize backend tensor/array/list outputs at the backend boundary. +- [x] 2.4 Keep benchmark episode identifiers and tick information in evaluation-layer request/record objects instead of policy observations. + +## 3. Remove unused contract description surface + +- [x] 3.1 Remove `RobotPolicyContractDescription`, `RobotPolicyContract.describe()`, and concrete/fake contract description methods. +- [x] 3.2 Stop writing `contract_description.json` in benchmark artifacts and update docs/tests accordingly. + +## 4. Validation and review follow-up + +- [x] 4.1 Update targeted unit tests for renamed models, moved modules, constrained backend outputs, and removed contract descriptions. +- [x] 4.2 Fix the md-babel docs snippet so executable docs no longer fail on missing imports. +- [x] 4.3 Run targeted policy rollout tests, ruff, and OpenSpec validation. +- [x] 4.4 Commit and push the review cleanup. +- [x] 4.5 Reply to addressed PR review comments and explicitly leave the image transport/performance question for separate design follow-up. diff --git a/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/.openspec.yaml b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/.openspec.yaml new file mode 100644 index 0000000000..e7cc357ca0 --- /dev/null +++ b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-01 diff --git a/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/design.md b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/design.md new file mode 100644 index 0000000000..c08f237f67 --- /dev/null +++ b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/design.md @@ -0,0 +1,68 @@ +## Context + +Simulator runtime execution has moved from HTTP sidecars to DimOS `Module` RPCs plus typed streams. The minimal merge kept policy rollout working by adapting module stream outputs back into the existing policy-evaluation `RuntimeClient` shape, including synthetic `data_ref` values and a `payload()` method. That compatibility layer is intentionally temporary: it allows the merge to stay small, but it preserves HTTP-era vocabulary and control flow in a code path that now uses placed runtime modules. + +Policy rollout needs two kinds of data per tick: + +- Synchronous reset/step metadata: reward, done, success, runtime description, and action protocol validation. +- Stream snapshots: camera images, camera metadata, and robot-state events published by the runtime module. + +The current seam mixes these by modeling stream payloads as if they were HTTP payload references. This change makes the module-native split explicit. + +## Goals / Non-Goals + +**Goals:** + +- Replace the HTTP-shaped `RuntimeClient.payload()` dependency with an explicit module-native runtime session or observation snapshot interface. +- Keep `BenchmarkPolicyEvalRunner` responsible for episode lifecycle, policy reset, action conversion, success gate, artifacts, and optional videos. +- Keep `RobotPolicyModule` focused on inference only. +- Keep the LeRobot LIBERO rollout demo on native LIBERO runtime actions and the placed `LiberoProRuntimeModule` path. +- Preserve current artifact compatibility where useful: summary, episodes JSONL, runtime description, checkpoint metadata, videos, and cleanup status. + +**Non-Goals:** + +- Redesign DimOS transport internals or image transport performance. +- Change the VLA-JEPA LIBERO contract semantics or action space. +- Reintroduce an HTTP sidecar server or HTTP payload endpoint. +- Require real LIBERO/LeRobot dependencies in normal unit tests. + +## Decisions + +### Use a module-native runtime session seam + +Create a runtime session abstraction for policy evaluation that owns the placed runtime module connection and exposes reset/step plus latest stream snapshots. For lockstep policy evaluation, the placed runtime module should also expose a deterministic latest-snapshot readback captured on the runtime owner side when it publishes stream values. The runner should receive observation values directly from the session instead of resolving `ObservationFrame.data_ref` through `payload()`. + +Alternative considered: keep synthetic `data_ref` values indefinitely. This avoids touching the runner, but keeps the wrong abstraction and makes future stream transport work harder to reason about. + +### Keep stream snapshot assembly outside the policy module + +The runtime module should publish `Image`, `CameraInfo`, and `ObservationFrame` events for stream consumers, and should retain the latest observation arrays/metadata for lockstep readback by the runtime session. The existing `LiberoRobotPolicyObservationBuilder` can then convert snapshot values and runtime metadata into `RobotPolicyObservation`. + +Alternative considered: let `RobotPolicyModule` subscribe to runtime streams directly. That would couple policy inference to benchmark runtime lifecycle and break the intended boundary where benchmark evaluation owns rollout orchestration. + +### Treat video capture as a consumer of stream snapshots + +Video writing should use the same image arrays that feed policy observations. This keeps video artifacts and policy input aligned and avoids a second data path. + +Alternative considered: keep separate video payload resolution through `data_ref`. That preserves duplicate data paths and hides when videos and policy observations diverge. + +### Remove HTTP-only rollout flags deliberately + +Once the runtime session seam is in place, `--runtime-host`, `--runtime-port`, `--startup-timeout-s`, and `--timeout-s` should be removed or explicitly deprecated if backward CLI compatibility is needed. Module runtime configuration should be expressed through runtime placement/environment options, camera options, benchmark selection, and rollout limits. + +Alternative considered: keep the flags as no-ops. That avoids breaking old command lines but leaves misleading knobs in the CLI. + +## Risks / Trade-offs + +- **Risk:** Stream callbacks and synchronous RPC responses may race in deployed workers. → **Mitigation:** capture latest stream snapshots on the runtime owner side and have policy evaluation read that deterministic snapshot after each reset or step. +- **Risk:** Removing `payload()` may require changes to fake runtime tests and helper objects. → **Mitigation:** update test doubles to implement the new session/snapshot seam directly, not an HTTP compatibility surface. +- **Risk:** CLI flag removal can break copied commands. → **Mitigation:** either keep deprecated aliases for one cycle with a warning, or document the replacement in the rollout docs. +- **Risk:** Snapshot naming may collapse multiple cameras if frame IDs are absent. → **Mitigation:** use `Image.frame_id` when present and require configured camera names for policy rollout. + +## Migration Plan + +1. Introduce the module-native runtime session/snapshot interface alongside the existing compatibility shim. +2. Update `BenchmarkPolicyEvalRunner` and tests to consume stream snapshots directly. +3. Update the LeRobot LIBERO rollout demo to use the new seam and remove synthetic payload generation. +4. Remove or deprecate HTTP-only rollout CLI flags. +5. Remove the old `payload()` compatibility path and stale docs/spec wording. diff --git a/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/proposal.md b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/proposal.md new file mode 100644 index 0000000000..8b762083ea --- /dev/null +++ b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/proposal.md @@ -0,0 +1,27 @@ +## Why + +The merge to module-native simulator runtimes removed the HTTP sidecar boundary, but policy evaluation still carries an HTTP-shaped `RuntimeClient.payload()` seam and synthetic payload references to bridge DimOS streams into policy observations. This makes the code harder to reason about and preserves transport vocabulary that no longer matches the runtime architecture. + +## What Changes + +- Replace the policy-evaluation runtime client seam with a module-native runtime session interface that returns synchronous reset/step metadata plus stream snapshots for policy-observation building. +- Move camera and state observation collection for policy rollout out of HTTP-style payload lookup and into explicit DimOS stream snapshot handling. +- Update the LeRobot LIBERO policy rollout demo to depend on the same module-native observation seam, while preserving native runtime actions, success-gate behavior, optional videos, and artifact outputs. +- Remove deprecated HTTP-shaped policy-evaluation fields, helper names, and compatibility shims after the stream snapshot seam is in place. + +## Capabilities + +### New Capabilities + +None. + +### Modified Capabilities + +- `benchmark-policy-evaluation`: Runtime observation input changes from sidecar payload references to module-native stream snapshots. +- `runtime-scripted-demos`: The LeRobot LIBERO policy rollout demo must use the native runtime module observation seam without HTTP-style runtime flags or payload fetching. + +## Impact + +- Affected code: `dimos/robot_learning/policy_rollout/evaluation.py`, `scripts/benchmarks/demo_lerobot_libero_policy_rollout.py`, related policy rollout tests, and possibly small runtime stream capture helpers. +- APIs: internal benchmark policy-evaluation runtime seam changes; public rollout CLI should keep policy/gate flags but may remove or deprecate HTTP-only runtime host/port/startup options. +- Dependencies: no new runtime dependencies expected. diff --git a/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/specs/benchmark-policy-evaluation/spec.md b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/specs/benchmark-policy-evaluation/spec.md new file mode 100644 index 0000000000..8504c24374 --- /dev/null +++ b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/specs/benchmark-policy-evaluation/spec.md @@ -0,0 +1,49 @@ +## MODIFIED Requirements + +### Requirement: Benchmark policy evaluation runner +The system SHALL provide a module-backed benchmark policy evaluation runner that owns episode matrix selection, runtime module reset and step calls, stream snapshot collection, scoring, success gates, metrics, artifacts, and cleanup for policy-driven benchmark rollouts. + +#### Scenario: Evaluation runner uses robot policy module for actions +- **WHEN** the evaluation runner has a current module-native observation snapshot during an episode +- **THEN** it builds a robot policy observation, sends the observation to the robot policy module through its module-facing inference API, adapts the returned robot policy action to a runtime action frame, and uses that frame for the runtime module step call + +#### Scenario: Evaluation runner owns episode lifecycle +- **WHEN** the evaluation runner starts an episode +- **THEN** it resets the runtime module, calls the robot policy module public reset method, steps the runtime module with adapted policy actions, records episode metrics, and applies the success gate outside the robot policy module + +#### Scenario: Evaluation runner can be launched through DimOS module composition +- **WHEN** a developer launches the LeRobot LIBERO policy evaluation path +- **THEN** benchmark evaluation, policy inference, and runtime module access are represented as DimOS modules or blueprint-compatible module configuration rather than only as directly wired plain Python service objects + +### Requirement: Rollout artifacts +The system SHALL write structured rollout artifacts that make policy, runtime, and episode behavior inspectable without storing full videos or image dumps by default. + +#### Scenario: Required metadata artifacts are written +- **WHEN** a policy rollout gate starts or completes +- **THEN** it writes `summary.json`, `episodes.jsonl`, `runtime_description.json`, and `checkpoint_metadata.json` in the artifact directory + +#### Scenario: Episode records include rollout diagnostics +- **WHEN** an episode record is written +- **THEN** it includes task index, init state index, episode id, success, step count, reward sum, done state, failure reason when present, action shape, action min/max, and observed stream names + +#### Scenario: Videos are opt-in +- **WHEN** the rollout is run without a video-saving option +- **THEN** it does not write full rollout videos or complete image dumps by default + +### Requirement: Benchmark sample builder +The system SHALL provide an explicit LIBERO benchmark sample-building seam that converts module-native runtime stream snapshots into robot policy observations. + +#### Scenario: Evaluation builds sample from runtime stream snapshot +- **WHEN** benchmark evaluation receives LIBERO runtime image streams, state events, and runtime metadata for an episode tick +- **THEN** it produces a robot policy observation with policy-facing observation roles, task context, timestamps when available, and metadata without fetching HTTP payload references + +#### Scenario: Sample builder remains outside policy module +- **WHEN** LIBERO runtime observations must be converted to robot policy observations +- **THEN** the conversion is owned by benchmark evaluation or its sample-builder seam rather than by the robot policy module + +### Requirement: Benchmark action adapter +The system SHALL adapt robot policy actions into native runtime action frames inside benchmark evaluation before stepping the runtime module. + +#### Scenario: Evaluation adapts policy action to runtime frame +- **WHEN** the robot policy module returns a robot policy action for the LIBERO native action surface +- **THEN** benchmark evaluation converts it into a runtime action frame with the matching action-space identity before calling the runtime module step method diff --git a/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/specs/runtime-scripted-demos/spec.md b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/specs/runtime-scripted-demos/spec.md new file mode 100644 index 0000000000..3bcd60bc5c --- /dev/null +++ b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/specs/runtime-scripted-demos/spec.md @@ -0,0 +1,28 @@ +## MODIFIED Requirements + +### Requirement: LeRobot LIBERO policy rollout demo +The system SHALL include a module-backed LeRobot LIBERO policy rollout demo that validates policy loading, contract conversion, native runtime actions, placed runtime-module stepping, stream snapshot collection, score collection, artifact output, and teardown through the same module-backed policy evaluation path used by DimOS workflow. + +#### Scenario: Policy rollout demo starts native runtime module and policy rollout stack +- **WHEN** a developer runs the LeRobot LIBERO policy rollout demo with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the demo starts the placed LIBERO runtime module in native LIBERO action mode, initializes module-backed benchmark evaluation with a robot policy module, LeRobot backend, and VLA-JEPA LIBERO contract, runs the configured episode matrix, writes artifacts, and tears down all resources + +#### Scenario: Policy rollout demo bypasses ControlCoordinator +- **WHEN** the LeRobot LIBERO policy rollout demo executes policy actions +- **THEN** actions flow from the robot policy module through benchmark evaluation to the runtime module as native runtime action frames without using ControlCoordinator, JointTrajectoryTask, EndEffectorDeltaTrajectoryTask, the SHM motor bridge, or motor action frames + +#### Scenario: Policy rollout demo consumes runtime streams directly +- **WHEN** the runtime module publishes configured camera images and robot-state events during reset or step +- **THEN** the policy rollout demo captures those DimOS stream outputs as the source of policy observations and optional videos without constructing HTTP payload references + +#### Scenario: Policy rollout demo enforces success gate +- **WHEN** the 50-episode policy rollout gate completes without setup or contract aborts +- **THEN** the demo passes only if the recorded success rate is greater than `0.50` + +#### Scenario: Policy rollout demo records required artifacts +- **WHEN** the LeRobot LIBERO policy rollout demo runs +- **THEN** it writes rollout summary, per-episode JSONL records, runtime description, checkpoint metadata, logs, and cleanup status to the artifact directory + +#### Scenario: Existing scripted demos remain unchanged +- **WHEN** the fake, Robosuite, or existing LIBERO-PRO motor demos are run +- **THEN** they continue to validate scripted runtime plumbing without requiring LeRobot policy dependencies or policy task success diff --git a/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/tasks.md b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/tasks.md new file mode 100644 index 0000000000..42ff505a4d --- /dev/null +++ b/openspec/changes/archive/2026-07-01-streamline-policy-eval-runtime-seam/tasks.md @@ -0,0 +1,26 @@ +## 1. Runtime Session Seam + +- [x] 1.1 Define a module-native policy evaluation runtime session interface for reset, step, runtime description, cleanup, and latest stream snapshots. +- [x] 1.2 Implement a LIBERO runtime-module session adapter that reads deterministic runtime-module image and runtime-event snapshots. +- [x] 1.3 Add fake runtime-session test helpers that model reset, step, stream snapshots, missing-stream cases, and cleanup without HTTP payload methods. + +## 2. Evaluation Refactor + +- [x] 2.1 Refactor `BenchmarkPolicyEvalRunner` to consume stream snapshots directly when building robot policy observations. +- [x] 2.2 Remove `RuntimeClient.payload()` and synthetic `data_ref` dependencies from policy rollout evaluation code. +- [x] 2.3 Keep episode lifecycle, policy reset, action adaptation, success gate, artifact writing, and optional video output behavior unchanged. +- [x] 2.4 Update rollout artifact records to report observed stream names from the module-native snapshot seam. + +## 3. Demo Cleanup + +- [x] 3.1 Update `demo_lerobot_libero_policy_rollout.py` to use the new runtime session adapter instead of its local payload compatibility shim. +- [x] 3.2 Remove or explicitly deprecate HTTP-only runtime CLI flags that no longer affect module-native rollout execution. +- [x] 3.3 Ensure the demo still passes native LIBERO action mode, camera width, camera height, benchmark, init-state, and video options into the runtime module path. + +## 4. Tests and Validation + +- [x] 4.1 Update policy rollout unit tests for direct stream snapshot observation building and missing-stream failure handling. +- [x] 4.2 Update runtime/demo tests so no test double implements or asserts `payload()` behavior. +- [x] 4.3 Run targeted policy rollout and runtime sidecar tests. +- [x] 4.4 Run ruff on changed policy rollout, runtime adapter, and demo files. +- [x] 4.5 Validate `streamline-policy-eval-runtime-seam` with OpenSpec strict validation. diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/.openspec.yaml b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/.openspec.yaml new file mode 100644 index 0000000000..8e26fbece7 --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-02 diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/design.md b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/design.md new file mode 100644 index 0000000000..9fb520cb7e --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/design.md @@ -0,0 +1,94 @@ +## Context + +The current LeRobot LIBERO policy gate is a synchronous simulator-evaluation path. Benchmark evaluation owns runtime reset/step, stream snapshot collection, observation building, action adaptation, scoring, artifacts, and cleanup. `RobotPolicyModule` owns backend lifecycle, policy reset, contract conversion, backend inference, and action emission through a synchronous module-facing inference API. + +That split is correct for fast and parallel simulation, but it does not model the real robot control topology. Real rollout should run from live policy observations, through policy chunk inference, into ControlCoordinator arbitration and control tasks. The live path should still be validated in LIBERO-PRO, but as a real-policy parity gate rather than a fake plumbing-only smoke test. + +## Goals / Non-Goals + +**Goals:** + +- Add a live policy stream path where `RobotPolicyModule` stores the latest `RobotPolicyObservation`, receives a fast chunk inference trigger, runs chunk inference asynchronously, and publishes `RobotPolicyActionChunk` messages. +- Make the LeRobot VLA-JEPA live path chunk-first by using `predict_action_chunk` and preserving the chunk shape until DimOS control code chooses how much to execute. +- Add ControlCoordinator support for policy action chunks without adding a separate conversion module. +- Add a policy chunk control task that owns index-bounded chunk execution, refill triggering, and stale deactivation. +- Add a module-native LIBERO live blueprint/demo path where the LIBERO runtime, observation assembly, `RobotPolicyModule`, and `ControlCoordinator` are DimOS modules wired through streams/RPC rather than local object callbacks. +- Add a LIBERO live parity gate that runs the real VLA-JEPA policy through the live stream and ControlCoordinator path over 10 `libero_object` episodes with `success_rate > 0.50` as a hard pass condition. +- Preserve the current synchronous benchmark path for faster-than-realtime, deterministic, and parallel simulator rollout. + +**Non-Goals:** + +- No removal of synchronous `infer_action` or the existing benchmark evaluation runner. +- No temporal ensembling, RTC-style chunk blending, or receding-horizon smoothing in v1. +- No new standalone module whose only job is converting policy action chunks into control commands. +- No fake-backend-only acceptance for the live parity gate. +- No redesign of high-throughput image transport or shared-memory observation delivery. + +## Decisions + +1. **Use dual surfaces on `RobotPolicyModule`.** + - Decision: keep synchronous inference for benchmark evaluation and add stream-native live rollout surfaces to the same module. + - Rationale: `RobotPolicyModule` is already the first-class policy module and owns action emission. A separate stream wrapper would create ambiguity over which module owns backend lifecycle and policy reset. + - Alternative considered: create `RobotPolicyStreamModule`. Rejected because it duplicates policy module responsibility and adds a wrapper layer without changing the core policy semantics. + +2. **Keep `RobotPolicyObservation` as the stream input boundary.** + - Decision: live `RobotPolicyModule` consumes ready `RobotPolicyObservation` messages, not raw camera or joint streams. + - Rationale: freshness, stream alignment, camera role selection, and task-language assembly are robot/runtime-specific. The policy module should consume a policy-ready observation and delegate backend-specific conversion to its contract. + - Alternative considered: make `RobotPolicyModule` subscribe directly to raw robot streams. Rejected because it would couple inference to sensor synchronization and robot-specific observation assembly. + +3. **Make live inference chunk-first.** + - Decision: live LeRobot inference uses `predict_action_chunk`, and DimOS introduces a first-class `RobotPolicyActionChunk` model. A single action is treated as a degenerate chunk of length one. + - Rationale: chunked policies predict a short horizon; DimOS should own how much of that horizon is executed. Flattening chunks into a single action hides execution policy and prevents ControlCoordinator from managing chunk refill and stale behavior. + - Alternative considered: continue using `select_action` for live rollout. Rejected because it leaves chunk execution inside the backend or policy library rather than the DimOS control stack. + +4. **Route live policy chunks into ControlCoordinator directly.** + - Decision: add a ControlCoordinator input for `RobotPolicyActionChunk` and route chunks to policy-aware control tasks. + - Rationale: the live path should use the same coordinator arbitration and control-task model as real robots. A separate conversion module would move execution semantics outside the coordinator. + - Alternative considered: adapt policy chunks to `JointState`, `PoseStamped`, or runtime action frames before ControlCoordinator. Rejected because it loses the semantic policy action surface too early. + +5. **Use a policy chunk control task with index-bounded execution.** + - Decision: the v1 task executes a configured number of leading actions from each chunk, advances by action index/tick-count convention, triggers refill when its queue is empty, and stops contributing commands when no fresh chunk is available. + - Rationale: index-bounded execution is simple and sufficient for v1. It avoids blindly executing full horizons while avoiding the complexity of time-window execution and overlap blending. + - Alternative considered: temporal ensembling or RTC-style blending. Rejected for v1 because it adds policy-specific complexity and makes the first live path harder to reason about. + +6. **Use fast trigger RPC and stream return for chunk refill.** + - Decision: when the policy chunk task needs a refill, it sends a fast trigger to `RobotPolicyModule`. The trigger returns early and does not perform backend inference synchronously. The inferred chunk returns on a stream. + - Rationale: ControlCoordinator tasks run in the coordinator tick loop; slow policy inference must not block the tick. The task still owns execution and refill timing, but the actual inference runs asynchronously in the policy module. + - Alternative considered: blocking inference RPC from task compute. Rejected because it can stall the entire coordinator tick loop. + +7. **Validate live LIBERO with the real policy.** + - Decision: the LIBERO live path acceptance run uses the real VLA-JEPA checkpoint over a 10-episode `libero_object` slice and passes only when `success_rate > 0.50`. + - Rationale: fake action plumbing is not enough to validate that the live stream and ControlCoordinator path preserves policy behavior. A 10-episode real-policy gate is large enough to catch live topology regressions while keeping the manual validation loop practical. + - Alternative considered: fake-backend smoke acceptance. Rejected because it does not validate policy timing, chunk inference, or semantic action execution. + +8. **Use module-native blueprint wiring for acceptance.** + - Decision: the accepting live parity path wires runtime observation assembly, `RobotPolicyModule`, and `ControlCoordinator` as DimOS modules. The coordinator/task requests refills from the policy module through the fast trigger RPC, and completed chunks return through the `RobotPolicyModule.policy_action_chunk` → `ControlCoordinator.robot_policy_action_chunk` stream. + - Rationale: the live rollout topology being validated is a module/stream/RPC topology, not a local Python object harness. The local harness may remain as a quick smoke path, but it must not be the only accepting path for this change. + - Alternative considered: keep direct local callback wiring in the parity script. Rejected because it bypasses blueprint stream wiring and does not exercise cross-module policy/coordinator boundaries. + +## Risks / Trade-offs + +- **Live path can stop between chunks while policy inference runs** → Accept for v1. The task stops contributing commands when empty/stale rather than continuing stale actions. +- **Chunk-first modeling changes backend and contract shapes** → Add explicit `RobotPolicyActionChunk` tests and keep single-action compatibility as a length-one chunk. +- **ControlCoordinator learns about robot-learning types** → Accept because policy chunks are a first-class live control input. Keep the dependency narrow to model types and task input routing. +- **Index-based execution assumes bounded coordinator tick jitter** → Use this for v1 simplicity and record tick/chunk diagnostics. Defer time-window execution until needed. +- **Real-policy live gate is expensive** → Keep the synchronous fast benchmark gate available and make the live gate explicit/manual like the existing LIBERO policy gate. +- **LIBERO live path may diverge from native direct-step performance** → Use a 10-episode success-rate gate and artifacts to diagnose chunk timing, refill delays, and stale deactivation. + +## Migration Plan + +1. Add model and interface support for `RobotPolicyActionChunk` while preserving `RobotPolicyAction` and synchronous inference. +2. Add stream and trigger surfaces to `RobotPolicyModule`, backed by the existing backend/contract lifecycle. +3. Update LeRobot backend/contract paths to preserve action chunk shape for live inference. +4. Add ControlCoordinator chunk input and policy chunk task registry entry. +5. Add LIBERO live observation assembly and ControlCoordinator runtime wiring. +6. Add module-native blueprint wiring for runtime observation assembly, policy module, and coordinator chunk routing. +7. Validate with unit tests, integration tests, OpenSpec validation, and the real-policy LIBERO live parity gate. + +Rollback is straightforward: the existing synchronous benchmark path remains intact and can continue to gate policy performance if the live path is disabled or removed. + +## Open Questions + +- What exact task type name and config names should the policy chunk task use? +- Should the initial LIBERO policy chunk task internally share code with Cartesian IK/teleop tasks, or duplicate only the minimal mapping needed for the gate? +- What artifacts should record chunk refill latency and stale deactivation counts in the live parity gate? diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/proposal.md b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/proposal.md new file mode 100644 index 0000000000..5c0c9763ff --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/proposal.md @@ -0,0 +1,31 @@ +## Why + +The current LeRobot LIBERO policy rollout path is optimized for simulator benchmark evaluation: benchmark code synchronously builds observations, calls policy inference, adapts one action, and steps the runtime. Real robot rollout needs a live stream topology where policy observations, policy chunks, and ControlCoordinator execution are first-class, while preserving the fast synchronous benchmark path for parallel and faster-than-realtime simulation. + +## What Changes + +- Add a stream-native surface to `RobotPolicyModule` that stores the latest `RobotPolicyObservation`, accepts a fast inference trigger, runs chunk inference asynchronously, and publishes `RobotPolicyActionChunk` results. +- Make LeRobot VLA-JEPA inference chunk-first by using `predict_action_chunk` for the live path and preserving the chunk shape through backend output, contract conversion, and policy module emission. +- Add a first-class `RobotPolicyActionChunk` model so DimOS, not the backend, owns chunk execution policy. +- Add a ControlCoordinator input for policy action chunks and a policy chunk control task that owns index-bounded chunk execution, asks the policy module to refill when empty, and stops contributing commands when no fresh chunk is available. +- Route the live LIBERO policy path through ControlCoordinator rather than adapting policy actions directly into runtime `step()` calls. +- Add a real-policy LIBERO live parity gate that runs the live policy stream path with the actual VLA-JEPA checkpoint and requires the same hard success condition as the existing LIBERO policy gate: `success_rate > 0.50`. +- Preserve the existing synchronous benchmark evaluation path for fast, deterministic, parallel simulator rollout. + +## Capabilities + +### New Capabilities + +- `policy-action-control`: ControlCoordinator support for receiving and executing robot policy action chunks through a policy-aware control task. + +### Modified Capabilities + +- `robot-policy-module`: Add stream-native observation ingestion, fast chunk inference triggering, chunk-first backend/contract output, and chunk stream emission while keeping synchronous inference available for benchmark evaluation. +- `runtime-scripted-demos`: Add a real-policy LIBERO live stream parity gate that validates policy rollout through ControlCoordinator rather than direct native runtime stepping. +- `benchmark-policy-evaluation`: Preserve the synchronous fast benchmark path and clarify its relationship to the live policy stream path. + +## Impact + +- Affected code: `dimos/robot_learning/policy_rollout/`, `dimos/control/`, `dimos/control/tasks/`, `scripts/benchmarks/`, LIBERO runtime demo/blueprint wiring, and associated tests. +- API impact: new policy action chunk model; new `RobotPolicyModule` stream/RPC surface; new ControlCoordinator policy chunk input; new control task type for policy chunk execution. +- Runtime impact: live LIBERO policy validation uses the real LeRobot VLA-JEPA checkpoint and ControlCoordinator path; the existing fast benchmark path remains available for simulator gates and parallel rollout. diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/benchmark-policy-evaluation/spec.md b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/benchmark-policy-evaluation/spec.md new file mode 100644 index 0000000000..c8ae8d7c89 --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/benchmark-policy-evaluation/spec.md @@ -0,0 +1,12 @@ +## ADDED Requirements + +### Requirement: Synchronous benchmark path preservation +The system SHALL preserve the synchronous benchmark policy evaluation path as the fast and deterministic simulator evaluation path even after adding the live policy stream path. + +#### Scenario: Benchmark runner keeps lockstep runtime stepping +- **WHEN** benchmark policy evaluation runs the existing LIBERO policy gate +- **THEN** benchmark evaluation owns runtime reset, snapshot collection, policy inference call, action adaptation, runtime step, scoring, artifacts, and cleanup in a lockstep loop without requiring ControlCoordinator policy chunk execution + +#### Scenario: Live path does not replace fast benchmark path +- **WHEN** both the synchronous benchmark gate and live policy stream parity gate exist +- **THEN** the synchronous benchmark gate remains available for faster-than-realtime or parallel simulator evaluation, while the live gate validates real-world rollout topology diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/policy-action-control/spec.md b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/policy-action-control/spec.md new file mode 100644 index 0000000000..8ce2fe0678 --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/policy-action-control/spec.md @@ -0,0 +1,49 @@ +## ADDED Requirements + +### Requirement: Policy action chunk coordinator input +The system SHALL allow ControlCoordinator to receive robot policy action chunks as first-class live control inputs and route them to policy-action-aware control tasks without requiring a separate conversion module. + +#### Scenario: Coordinator routes policy action chunks to task +- **WHEN** a robot policy module publishes a valid robot policy action chunk to the coordinator policy chunk input +- **THEN** ControlCoordinator delivers the chunk to a configured policy chunk control task that accepts the chunk action-space identity + +#### Scenario: Invalid policy chunk is rejected before execution +- **WHEN** a policy chunk has an unsupported action-space id, incompatible shape, non-finite value, or value outside the task's accepted normalized range +- **THEN** the policy chunk control task rejects the chunk and does not emit control commands from it + +### Requirement: Index-bounded policy chunk execution +The system SHALL provide a ControlCoordinator policy chunk control task that owns index-bounded execution of robot policy action chunks. + +#### Scenario: Task executes configured leading actions +- **WHEN** the policy chunk control task receives a chunk with more actions than its configured execution count +- **THEN** it executes only the configured leading actions before requesting or waiting for a replacement chunk + +#### Scenario: Task advances chunk execution by coordinator ticks +- **WHEN** the policy chunk control task is active during ControlCoordinator ticks +- **THEN** it advances through the selected chunk actions according to its configured tick-count execution cadence + +#### Scenario: Task stops contributing when chunk is stale +- **WHEN** the policy chunk control task has no fresh chunk or its active chunk exceeds the configured staleness limit +- **THEN** it stops contributing commands to ControlCoordinator arbitration rather than continuing stale policy actions + +### Requirement: Policy chunk refill trigger +The system SHALL allow a policy chunk control task to request a new policy action chunk from the robot policy module through a fast inference trigger that does not block ControlCoordinator on backend inference. + +#### Scenario: Empty task triggers policy inference +- **WHEN** the policy chunk control task consumes its configured chunk prefix and has no replacement chunk available +- **THEN** it sends a fast inference trigger to the robot policy module and returns no control command while waiting for a stream-delivered chunk + +#### Scenario: Trigger returns before backend inference completes +- **WHEN** the policy chunk control task sends the inference trigger from its control execution path +- **THEN** the trigger returns without running the slow backend inference synchronously in the ControlCoordinator tick loop + +### Requirement: Policy chunk task maps normalized actions to coordinator commands +The system SHALL let the policy chunk control task own physical execution mapping from normalized robot policy action values to ControlCoordinator joint or gripper commands. + +#### Scenario: LIBERO-style end-effector delta action is mapped inside task +- **WHEN** the policy chunk task is configured for normalized end-effector delta plus gripper actions +- **THEN** it maps the selected normalized action into coordinator-compatible arm and gripper commands inside the task rather than requiring an external conversion module + +#### Scenario: Policy module remains outside control execution mapping +- **WHEN** the robot policy module emits a normalized robot policy action chunk +- **THEN** it does not denormalize the action into physical controller units or call runtime/control execution APIs directly diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/robot-policy-module/spec.md b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/robot-policy-module/spec.md new file mode 100644 index 0000000000..d4405d3f37 --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/robot-policy-module/spec.md @@ -0,0 +1,93 @@ +## MODIFIED Requirements + +### Requirement: Robot policy module inference boundary +The system SHALL provide a robot policy module that is a first-class DimOS `Module` and owns policy backend lifecycle, public policy reset, robot policy contract conversion, backend inference, robot policy action emission, and live policy action chunk emission without owning benchmark episode lifecycle, runtime reset or step calls, ControlCoordinator control execution, scoring, success gates, or artifact writing. + +#### Scenario: Robot policy module emits one action through the architecture seams +- **WHEN** the robot policy module receives a ready robot policy observation through its synchronous inference API and has an initialized policy backend +- **THEN** it converts the observation through the robot policy contract, invokes the backend through `infer_batch`, converts the backend output back through the contract, and returns or emits the resulting robot policy action + +#### Scenario: Robot policy module emits action chunks through live stream seams +- **WHEN** the robot policy module receives a fast live inference trigger and has a latest ready robot policy observation +- **THEN** it converts the observation through the robot policy contract, invokes chunk-capable backend inference asynchronously, converts the backend output into a robot policy action chunk, and publishes the chunk on its live action chunk stream + +#### Scenario: Robot policy module exposes public reset +- **WHEN** a benchmark runner or live rollout path starts a new policy episode +- **THEN** it can call the robot policy module public reset method, and the robot policy module resets backend episode state before the next inference + +#### Scenario: Robot policy module is blueprint-configured +- **WHEN** a blueprint constructs the robot policy module +- **THEN** the blueprint supplies backend and contract types and parameters as module configuration rather than passing live backend or contract objects + +### Requirement: Batch-first policy backend +The system SHALL define a policy backend interface whose inference methods accept backend-ready batches and return backend output envelopes containing finite numeric action values or finite numeric action chunks plus inference metadata. + +#### Scenario: Robot policy module does not depend on LeRobot select_action +- **WHEN** the robot policy module needs a policy action or action chunk +- **THEN** it calls the backend batch inference method rather than depending directly on LeRobot-specific `select_action` or `predict_action_chunk` APIs + +#### Scenario: Backend describes loaded policy metadata +- **WHEN** a policy backend is initialized +- **THEN** it can describe backend type, checkpoint identifier, resolved checkpoint metadata when available, device, policy class when available, episode reset support, and chunk inference support for rollout artifacts + +#### Scenario: Backend output is constrained +- **WHEN** a policy backend completes inference +- **THEN** it returns a backend output envelope whose output is finite numeric action data suitable for contract validation, preserving chunk rank when chunk inference is requested + +### Requirement: VLA-JEPA LIBERO robot policy contract +The system SHALL provide a narrow robot policy contract for the VLA-JEPA LIBERO rollout that converts robot policy observations into LeRobot backend batches and converts LeRobot backend output into robot policy actions or robot policy action chunks. + +#### Scenario: Contract maps robot policy observation to LeRobot batch +- **WHEN** the contract receives a ready robot policy observation for VLA-JEPA LIBERO rollout +- **THEN** it maps agent-view camera data to `observation.images.image`, wrist or eye-in-hand camera data to `observation.images.image2`, the 8D robot state to `observation.state`, and language metadata or observation data to the backend prompt field expected by the backend path + +#### Scenario: Contract rejects semantic input mismatch +- **WHEN** a supposedly ready observation is missing a required role or has incompatible image, state, dtype, shape, or language semantics +- **THEN** the contract raises a contract conversion failure before backend inference runs + +#### Scenario: Contract converts backend output to robot policy action +- **WHEN** the contract receives a backend output envelope containing a valid VLA-JEPA LIBERO action tuple +- **THEN** it returns a robot policy action with action-space id `libero.ee_delta_6d_gripper.normalized.v1` and finite `float32[7]` values compatible with the native LIBERO action range + +#### Scenario: Contract converts backend output to robot policy action chunk +- **WHEN** the contract receives a backend output envelope containing a valid VLA-JEPA LIBERO action chunk +- **THEN** it returns a robot policy action chunk with action-space id `libero.ee_delta_6d_gripper.normalized.v1` and finite normalized `float32[N,7]` values compatible with DimOS-owned chunk execution + +### Requirement: Robot policy action boundary +The system SHALL define reusable robot policy action and robot policy action chunk models for policy inference outputs before adaptation to runtime frames, ControlCoordinator task commands, or real robot control commands. + +#### Scenario: Policy module returns runtime-independent action +- **WHEN** a robot policy module completes single-action inference and contract conversion +- **THEN** it returns a robot policy action with action-space identity, numeric values, and metadata rather than a runtime sidecar action frame + +#### Scenario: Policy module publishes runtime-independent action chunk +- **WHEN** a robot policy module completes live chunk inference and contract conversion +- **THEN** it publishes a robot policy action chunk with action-space identity, ordered numeric action values, and metadata rather than controller-ready joint commands or runtime action frames + +## ADDED Requirements + +### Requirement: Live policy observation stream +The system SHALL allow a robot policy module to receive live robot policy observations through a stream and retain the latest ready observation for asynchronously triggered chunk inference. + +#### Scenario: Module stores latest live observation +- **WHEN** a robot policy observation arrives on the live policy observation stream +- **THEN** the robot policy module records it as the latest candidate observation for future chunk inference triggers + +#### Scenario: Trigger fails clearly without observation +- **WHEN** a live chunk inference trigger arrives before any ready robot policy observation has been received +- **THEN** the robot policy module reports a clear not-ready result and does not run backend inference + +### Requirement: Fast live chunk inference trigger +The system SHALL expose a fast trigger on the robot policy module that requests asynchronous chunk inference from the latest live observation without returning the chunk as the trigger response. + +#### Scenario: Trigger starts asynchronous inference +- **WHEN** the robot policy module receives a live chunk inference trigger and has a latest ready observation +- **THEN** it starts or schedules backend chunk inference and returns before the chunk result is available + +#### Scenario: Chunk result returns on stream +- **WHEN** asynchronous chunk inference completes successfully +- **THEN** the robot policy module publishes the resulting robot policy action chunk on its chunk output stream + +#### Scenario: Duplicate trigger does not start concurrent backend inference +- **WHEN** a trigger arrives while chunk inference is already in flight for the same module instance +- **THEN** the robot policy module does not start an unbounded concurrent backend inference request diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/runtime-scripted-demos/spec.md b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/runtime-scripted-demos/spec.md new file mode 100644 index 0000000000..fe693e2437 --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/specs/runtime-scripted-demos/spec.md @@ -0,0 +1,24 @@ +## ADDED Requirements + +### Requirement: LeRobot LIBERO live policy stream parity gate +The system SHALL include a script-based LeRobot LIBERO live policy stream parity gate that validates the real VLA-JEPA policy through module-native RobotPolicyModule live chunk inference, ControlCoordinator policy chunk execution, LIBERO runtime observation streams, score collection, artifact output, and teardown. + +#### Scenario: Live gate runs the real policy +- **WHEN** a developer runs the LeRobot LIBERO live policy stream parity gate with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the gate loads the actual `lerobot/VLA-JEPA-LIBERO` policy rather than using fake or fixed policy actions as the acceptance path + +#### Scenario: Live gate routes policy through ControlCoordinator +- **WHEN** the live parity gate executes policy actions +- **THEN** observations flow into RobotPolicyModule, inferred robot policy action chunks flow through ControlCoordinator, and the policy chunk control task drives the runtime control path rather than benchmark evaluation adapting actions directly into native runtime `step()` calls + +#### Scenario: Live gate uses module-native wiring +- **WHEN** the live parity gate wires the runtime observation stream, policy module, and coordinator +- **THEN** `RobotPolicyModule.policy_action_chunk` is connected to `ControlCoordinator.robot_policy_action_chunk` through a module stream boundary, and the coordinator requests refills through the policy module fast trigger RPC rather than through direct local callback execution + +#### Scenario: Live gate enforces 10-episode real-policy success +- **WHEN** the 10-episode live parity gate over `libero_object` completes without setup or contract aborts +- **THEN** the gate passes only if the recorded success rate is greater than `0.50` + +#### Scenario: Live gate records chunk diagnostics +- **WHEN** the LeRobot LIBERO live parity gate runs +- **THEN** it writes rollout artifacts that include policy success metrics and live-path diagnostics such as chunk counts, refill triggers, consumed actions, stale deactivations, and cleanup status diff --git a/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/tasks.md b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/tasks.md new file mode 100644 index 0000000000..075480b4f5 --- /dev/null +++ b/openspec/changes/archive/2026-07-02-add-live-policy-stream-rollout/tasks.md @@ -0,0 +1,47 @@ +## 1. Policy chunk models and backend contracts + +- [x] 1.1 Add a first-class `RobotPolicyActionChunk` model with action-space id, ordered action values, sequence/timing metadata, and validation-friendly shape accessors. +- [x] 1.2 Extend backend output modeling so single-action inference and chunk inference preserve rank instead of flattening chunks into one action tuple. +- [x] 1.3 Add LeRobot backend support for chunk-first live inference using `predict_action_chunk` while preserving synchronous single-action inference for the fast benchmark path. +- [x] 1.4 Update the VLA-JEPA LIBERO contract to convert valid `N x 7` normalized backend chunks into `RobotPolicyActionChunk` values. +- [x] 1.5 Add unit tests for chunk model validation, backend chunk rank preservation, and contract chunk conversion failures. + +## 2. RobotPolicyModule live stream surface + +- [x] 2.1 Add typed stream outputs/inputs for live robot policy observations and robot policy action chunks on `RobotPolicyModule`. +- [x] 2.2 Store the latest live `RobotPolicyObservation` received by the module without making the policy module subscribe to raw camera or joint streams. +- [x] 2.3 Add a fast chunk inference trigger RPC that returns before slow backend inference completes and reports clear not-ready behavior when no observation is available. +- [x] 2.4 Run triggered chunk inference asynchronously with at most one in-flight backend inference per module instance by default. +- [x] 2.5 Publish successful chunk inference results on the policy action chunk stream and expose status/error metadata for diagnostics. +- [x] 2.6 Preserve existing synchronous `reset`, `infer_action`, `describe_backend`, and benchmark-facing behavior. +- [x] 2.7 Add module tests for latest-observation storage, trigger behavior, no-observation failure, duplicate trigger handling, chunk publication, reset, and backward-compatible synchronous inference. + +## 3. ControlCoordinator policy chunk execution + +- [x] 3.1 Add a ControlCoordinator input stream for `RobotPolicyActionChunk` values and route chunks to policy-action-aware tasks. +- [x] 3.2 Add a policy chunk control task registry entry and task configuration for accepted action-space id, claimed joints, priority, ticks per action, execute-first-N count, stale timeout, and gripper mapping. +- [x] 3.3 Implement policy chunk task validation for action-space id, shape, finite normalized values, and supported action dimensions. +- [x] 3.4 Implement index-bounded chunk execution that consumes a configured leading prefix and emits coordinator-compatible commands on each tick. +- [x] 3.5 Implement queue-empty refill behavior using the fast policy chunk inference trigger without blocking backend inference in the coordinator tick loop. +- [x] 3.6 Implement stale deactivation so the task stops contributing commands after configured staleness rather than continuing stale policy actions. +- [x] 3.7 Add ControlCoordinator/task tests for chunk routing, validation failures, prefix execution, refill trigger calls, stale deactivation, and arbitration behavior. + +## 4. LIBERO live policy stream path + +- [x] 4.1 Add a LIBERO live observation assembler that converts runtime image/state streams and task metadata into `RobotPolicyObservation` messages for the policy module. +- [x] 4.2 Wire LIBERO runtime observation streams, `RobotPolicyModule`, ControlCoordinator, and the policy chunk control task into a live parity demo path. +- [x] 4.3 Ensure the live path uses the real `lerobot/VLA-JEPA-LIBERO` checkpoint and chunk inference rather than fake/fixed policy actions for acceptance. +- [x] 4.4 Route policy execution through ControlCoordinator and the policy chunk task rather than direct benchmark evaluation adaptation into native runtime `step()` calls. +- [x] 4.5 Record live-path diagnostics including chunk counts, refill triggers, consumed actions, stale deactivations, inference status, and cleanup status. +- [x] 4.6 Add focused integration tests with fakes for the live observation-to-policy-to-coordinator-to-runtime control flow. +- [x] 4.7 Add module-native LIBERO live wiring so runtime observation assembly, `RobotPolicyModule`, and `ControlCoordinator` run as DimOS modules connected by streams/RPC rather than local object callbacks. +- [x] 4.8 Add tests proving `RobotPolicyModule.policy_action_chunk` connects to `ControlCoordinator.robot_policy_action_chunk` through module stream wiring and coordinator refill requests call the policy module trigger. + +## 5. Benchmark preservation and validation + +- [x] 5.1 Keep the existing synchronous benchmark policy evaluation path runnable without requiring ControlCoordinator policy chunk execution. +- [x] 5.2 Add tests proving the fast benchmark path still uses lockstep runtime reset/snapshot/inference/action-adaptation/step ownership. +- [x] 5.3 Run targeted unit tests for policy models, backend, contract, policy module, ControlCoordinator task, and benchmark evaluation. +- [x] 5.4 Run OpenSpec validation for `add-live-policy-stream-rollout` in strict mode. +- [x] 5.5 Run the module-native real-policy LIBERO live parity gate over a 10-episode `libero_object` slice and require `success_rate > 0.50`. +- [x] 5.6 Update developer documentation for the difference between the fast benchmark gate and live policy stream parity gate. diff --git a/openspec/specs/benchmark-policy-evaluation/spec.md b/openspec/specs/benchmark-policy-evaluation/spec.md new file mode 100644 index 0000000000..b918c272fe --- /dev/null +++ b/openspec/specs/benchmark-policy-evaluation/spec.md @@ -0,0 +1,84 @@ +# benchmark-policy-evaluation Specification + +## Purpose +TBD - created by archiving change add-lerobot-libero-policy-rollout. Update Purpose after archive. + +## Requirements + +### Requirement: Benchmark policy evaluation runner +The system SHALL provide a module-backed benchmark policy evaluation runner that owns episode matrix selection, runtime module reset and step calls, stream snapshot collection, scoring, success gates, metrics, artifacts, and cleanup for policy-driven benchmark rollouts. + +#### Scenario: Evaluation runner uses robot policy module for actions +- **WHEN** the evaluation runner has a current module-native observation snapshot during an episode +- **THEN** it builds a robot policy observation, sends the observation to the robot policy module through its module-facing inference API, adapts the returned robot policy action to a runtime action frame, and uses that frame for the runtime module step call + +#### Scenario: Evaluation runner owns episode lifecycle +- **WHEN** the evaluation runner starts an episode +- **THEN** it resets the runtime module, calls the robot policy module public reset method, steps the runtime module with adapted policy actions, records episode metrics, and applies the success gate outside the robot policy module + +#### Scenario: Evaluation runner can be launched through DimOS module composition +- **WHEN** a developer launches the LeRobot LIBERO policy evaluation path +- **THEN** benchmark evaluation, policy inference, and runtime module access are represented as DimOS modules or blueprint-compatible module configuration rather than only as directly wired plain Python service objects + +### Requirement: LIBERO policy rollout gate +The system SHALL provide a policy-driven LIBERO gate using `lerobot/VLA-JEPA-LIBERO` over `libero_object`, all 10 task indices, and init states `[0, 1, 2, 3, 4]`, for 50 total episodes with pass condition `success_rate > 0.50`. + +#### Scenario: Gate evaluates the configured episode matrix +- **WHEN** the policy rollout gate is run with prepared LIBERO assets and compatible LeRobot dependencies +- **THEN** it evaluates 50 episodes formed from all 10 `libero_object` task indices crossed with init states `[0, 1, 2, 3, 4]` + +#### Scenario: Gate records pass/fail summary +- **WHEN** all scheduled episodes complete without setup or contract aborts +- **THEN** the gate writes a summary containing episode count, success count, success rate, threshold, and pass/fail result + +#### Scenario: Policy failures continue the run +- **WHEN** an individual episode times out, reaches done without success, or reports unsuccessful completion +- **THEN** the episode is recorded as a failed policy episode and the rollout continues with the next scheduled episode + +#### Scenario: Setup and contract errors abort the run +- **WHEN** checkpoint loading, sidecar compatibility, action spec validation, observation mapping, action conversion, or protocol validation fails +- **THEN** the rollout aborts the run and records the failure as an integration/setup error rather than counting it as a policy episode failure + +### Requirement: Rollout artifacts +The system SHALL write structured rollout artifacts that make policy, runtime, and episode behavior inspectable without storing full videos or image dumps by default. + +#### Scenario: Required metadata artifacts are written +- **WHEN** a policy rollout gate starts or completes +- **THEN** it writes `summary.json`, `episodes.jsonl`, `runtime_description.json`, and `checkpoint_metadata.json` in the artifact directory + +#### Scenario: Episode records include rollout diagnostics +- **WHEN** an episode record is written +- **THEN** it includes task index, init state index, episode id, success, step count, reward sum, done state, failure reason when present, action shape, action min/max, and observed stream names + +#### Scenario: Videos are opt-in +- **WHEN** the rollout is run without a video-saving option +- **THEN** it does not write full rollout videos or complete image dumps by default + +### Requirement: Benchmark sample builder +The system SHALL provide an explicit LIBERO benchmark sample-building seam that converts module-native runtime stream snapshots into robot policy observations. + +#### Scenario: Evaluation builds sample from runtime stream snapshot +- **WHEN** benchmark evaluation receives LIBERO runtime image streams, state events, and runtime metadata for an episode tick +- **THEN** it produces a robot policy observation with policy-facing observation roles, task context, timestamps when available, and metadata without fetching HTTP payload references + +#### Scenario: Sample builder remains outside policy module +- **WHEN** LIBERO runtime observations must be converted to robot policy observations +- **THEN** the conversion is owned by benchmark evaluation or its sample-builder seam rather than by the robot policy module + +### Requirement: Benchmark action adapter +The system SHALL adapt robot policy actions into native runtime action frames inside benchmark evaluation before stepping the runtime module. + +#### Scenario: Evaluation adapts policy action to runtime frame +- **WHEN** the robot policy module returns a robot policy action for the LIBERO native action surface +- **THEN** benchmark evaluation converts it into a runtime action frame with the matching action-space identity before calling the runtime module step method + +### Requirement: Synchronous benchmark path preservation +The system SHALL preserve the synchronous benchmark policy evaluation path as the fast and deterministic simulator evaluation path even after adding the live policy stream path. + +#### Scenario: Benchmark runner keeps lockstep runtime stepping +- **WHEN** benchmark policy evaluation runs the existing LIBERO policy gate +- **THEN** benchmark evaluation owns runtime reset, snapshot collection, policy inference call, action adaptation, runtime step, scoring, artifacts, and cleanup in a lockstep loop without requiring ControlCoordinator policy chunk execution + +#### Scenario: Live path does not replace fast benchmark path +- **WHEN** both the synchronous benchmark gate and live policy stream parity gate exist +- **THEN** the synchronous benchmark gate remains available for faster-than-realtime or parallel simulator evaluation, while the live gate validates real-world rollout topology diff --git a/openspec/specs/policy-action-control/spec.md b/openspec/specs/policy-action-control/spec.md new file mode 100644 index 0000000000..7bec4c81e9 --- /dev/null +++ b/openspec/specs/policy-action-control/spec.md @@ -0,0 +1,54 @@ +# policy-action-control Specification + +## Purpose +TBD - created by archiving change add-live-policy-stream-rollout. Update Purpose after archive. + +## Requirements + +### Requirement: Policy action chunk coordinator input +The system SHALL allow ControlCoordinator to receive robot policy action chunks as first-class live control inputs and route them to policy-action-aware control tasks without requiring a separate conversion module. + +#### Scenario: Coordinator routes policy action chunks to task +- **WHEN** a robot policy module publishes a valid robot policy action chunk to the coordinator policy chunk input +- **THEN** ControlCoordinator delivers the chunk to a configured policy chunk control task that accepts the chunk action-space identity + +#### Scenario: Invalid policy chunk is rejected before execution +- **WHEN** a policy chunk has an unsupported action-space id, incompatible shape, non-finite value, or value outside the task's accepted normalized range +- **THEN** the policy chunk control task rejects the chunk and does not emit control commands from it + +### Requirement: Index-bounded policy chunk execution +The system SHALL provide a ControlCoordinator policy chunk control task that owns index-bounded execution of robot policy action chunks. + +#### Scenario: Task executes configured leading actions +- **WHEN** the policy chunk control task receives a chunk with more actions than its configured execution count +- **THEN** it executes only the configured leading actions before requesting or waiting for a replacement chunk + +#### Scenario: Task advances chunk execution by coordinator ticks +- **WHEN** the policy chunk control task is active during ControlCoordinator ticks +- **THEN** it advances through the selected chunk actions according to its configured tick-count execution cadence + +#### Scenario: Task stops contributing when chunk is stale +- **WHEN** the policy chunk control task has no fresh chunk or its active chunk exceeds the configured staleness limit +- **THEN** it stops contributing commands to ControlCoordinator arbitration rather than continuing stale policy actions + +### Requirement: Policy chunk refill trigger +The system SHALL allow a policy chunk control task to request a new policy action chunk from the robot policy module through a fast inference trigger that does not block ControlCoordinator on backend inference. + +#### Scenario: Empty task triggers policy inference +- **WHEN** the policy chunk control task consumes its configured chunk prefix and has no replacement chunk available +- **THEN** it sends a fast inference trigger to the robot policy module and returns no control command while waiting for a stream-delivered chunk + +#### Scenario: Trigger returns before backend inference completes +- **WHEN** the policy chunk control task sends the inference trigger from its control execution path +- **THEN** the trigger returns without running the slow backend inference synchronously in the ControlCoordinator tick loop + +### Requirement: Policy chunk task maps normalized actions to coordinator commands +The system SHALL let the policy chunk control task own physical execution mapping from normalized robot policy action values to ControlCoordinator joint or gripper commands. + +#### Scenario: LIBERO-style end-effector delta action is mapped inside task +- **WHEN** the policy chunk task is configured for normalized end-effector delta plus gripper actions +- **THEN** it maps the selected normalized action into coordinator-compatible arm and gripper commands inside the task rather than requiring an external conversion module + +#### Scenario: Policy module remains outside control execution mapping +- **WHEN** the robot policy module emits a normalized robot policy action chunk +- **THEN** it does not denormalize the action into physical controller units or call runtime/control execution APIs directly diff --git a/openspec/specs/robot-policy-module/spec.md b/openspec/specs/robot-policy-module/spec.md new file mode 100644 index 0000000000..ebe065c8e1 --- /dev/null +++ b/openspec/specs/robot-policy-module/spec.md @@ -0,0 +1,140 @@ +# robot-policy-module Specification + +## Purpose +TBD - created by archiving change add-lerobot-libero-policy-rollout. Update Purpose after archive. + +## Requirements + +### Requirement: Robot policy module inference boundary +The system SHALL provide a robot policy module that is a first-class DimOS `Module` and owns policy backend lifecycle, public policy reset, robot policy contract conversion, backend inference, robot policy action emission, and live policy action chunk emission without owning benchmark episode lifecycle, runtime reset or step calls, ControlCoordinator control execution, scoring, success gates, or artifact writing. + +#### Scenario: Robot policy module emits one action through the architecture seams +- **WHEN** the robot policy module receives a ready robot policy observation through its synchronous inference API and has an initialized policy backend +- **THEN** it converts the observation through the robot policy contract, invokes the backend through `infer_batch`, converts the backend output back through the contract, and returns or emits the resulting robot policy action + +#### Scenario: Robot policy module emits action chunks through live stream seams +- **WHEN** the robot policy module receives a fast live inference trigger and has a latest ready robot policy observation +- **THEN** it converts the observation through the robot policy contract, invokes chunk-capable backend inference asynchronously, converts the backend output into a robot policy action chunk, and publishes the chunk on its live action chunk stream + +#### Scenario: Robot policy module exposes public reset +- **WHEN** a benchmark runner or live rollout path starts a new policy episode +- **THEN** it can call the robot policy module public reset method, and the robot policy module resets backend episode state before the next inference + +#### Scenario: Robot policy module is blueprint-configured +- **WHEN** a blueprint constructs the robot policy module +- **THEN** the blueprint supplies backend and contract types and parameters as module configuration rather than passing live backend or contract objects + +### Requirement: Batch-first policy backend +The system SHALL define a policy backend interface whose inference methods accept backend-ready batches and return backend output envelopes containing finite numeric action values or finite numeric action chunks plus inference metadata. + +#### Scenario: Robot policy module does not depend on LeRobot select_action +- **WHEN** the robot policy module needs a policy action or action chunk +- **THEN** it calls the backend batch inference method rather than depending directly on LeRobot-specific `select_action` or `predict_action_chunk` APIs + +#### Scenario: Backend describes loaded policy metadata +- **WHEN** a policy backend is initialized +- **THEN** it can describe backend type, checkpoint identifier, resolved checkpoint metadata when available, device, policy class when available, episode reset support, and chunk inference support for rollout artifacts + +#### Scenario: Backend output is constrained +- **WHEN** a policy backend completes inference +- **THEN** it returns a backend output envelope whose output is finite numeric action data suitable for contract validation, preserving chunk rank when chunk inference is requested + +### Requirement: LeRobot backend for VLA-JEPA LIBERO +The system SHALL provide an in-process LeRobot backend capable of loading and running the official `lerobot/VLA-JEPA-LIBERO` checkpoint for LIBERO policy rollout using official LeRobot policy and processor APIs. + +#### Scenario: Backend initializes official checkpoint +- **WHEN** the LeRobot backend is configured with `lerobot/VLA-JEPA-LIBERO` +- **THEN** it loads the policy through the official VLA-JEPA policy API, prepares required LeRobot preprocessing and postprocessing through official processor factories, moves the policy to the configured device, and enters inference/evaluation mode + +#### Scenario: Backend returns an output envelope +- **WHEN** the LeRobot backend completes inference for a backend-ready batch +- **THEN** it returns a backend output envelope containing a flat action tuple and inference metadata needed for validation and artifacts + +### Requirement: VLA-JEPA LIBERO robot policy contract +The system SHALL provide a narrow robot policy contract for the VLA-JEPA LIBERO rollout that converts robot policy observations into LeRobot backend batches and converts LeRobot backend output into robot policy actions or robot policy action chunks. + +#### Scenario: Contract maps robot policy observation to LeRobot batch +- **WHEN** the contract receives a ready robot policy observation for VLA-JEPA LIBERO rollout +- **THEN** it maps agent-view camera data to `observation.images.image`, wrist or eye-in-hand camera data to `observation.images.image2`, the 8D robot state to `observation.state`, and language metadata or observation data to the backend prompt field expected by the backend path + +#### Scenario: Contract rejects semantic input mismatch +- **WHEN** a supposedly ready observation is missing a required role or has incompatible image, state, dtype, shape, or language semantics +- **THEN** the contract raises a contract conversion failure before backend inference runs + +#### Scenario: Contract converts backend output to robot policy action +- **WHEN** the contract receives a backend output envelope containing a valid VLA-JEPA LIBERO action tuple +- **THEN** it returns a robot policy action with action-space id `libero.ee_delta_6d_gripper.normalized.v1` and finite `float32[7]` values compatible with the native LIBERO action range + +#### Scenario: Contract converts backend output to robot policy action chunk +- **WHEN** the contract receives a backend output envelope containing a valid VLA-JEPA LIBERO action chunk +- **THEN** it returns a robot policy action chunk with action-space id `libero.ee_delta_6d_gripper.normalized.v1` and finite normalized `float32[N,7]` values compatible with DimOS-owned chunk execution + +### Requirement: Policy backend registry +The system SHALL provide a lazy policy backend registry that maps backend type names to importable backend factory paths. + +#### Scenario: Module creates backend from registry +- **WHEN** a robot policy module is configured with a registered backend type and backend parameters +- **THEN** it resolves the backend factory lazily and constructs the policy backend from configuration rather than requiring a live backend object in the blueprint + +#### Scenario: Unknown backend type fails clearly +- **WHEN** a robot policy module is configured with an unknown backend type +- **THEN** backend construction fails with a message that includes the unknown type and the available backend types + +### Requirement: Robot policy contract registry +The system SHALL provide a lazy robot policy contract registry that maps contract type names to importable contract factory paths. + +#### Scenario: Module creates contract from registry +- **WHEN** a robot policy module is configured with a registered contract type and contract parameters +- **THEN** it resolves the contract factory lazily and constructs the robot policy contract from configuration rather than requiring a live contract object in the blueprint + +#### Scenario: Unknown contract type fails clearly +- **WHEN** a robot policy module is configured with an unknown contract type +- **THEN** contract construction fails with a message that includes the unknown type and the available contract types + +### Requirement: Robot learning sample boundary +The system SHALL define a reusable robot policy observation model for policy inference inputs that is not tied to a benchmark sidecar response type or benchmark episode lifecycle identifiers. + +#### Scenario: Policy module accepts runtime-independent observations +- **WHEN** a robot policy module receives an inference request +- **THEN** the request carries a robot policy observation with semantically named observation roles, timestamps, and metadata rather than a benchmark-specific runtime observation sample or benchmark episode identifiers + +#### Scenario: Language remains contract-specific input data +- **WHEN** a policy contract requires a language prompt +- **THEN** it reads the prompt from observation metadata or observation roles rather than from a generic top-level task field on every policy observation + +### Requirement: Robot policy action boundary +The system SHALL define reusable robot policy action and robot policy action chunk models for policy inference outputs before adaptation to runtime frames, ControlCoordinator task commands, or real robot control commands. + +#### Scenario: Policy module returns runtime-independent action +- **WHEN** a robot policy module completes inference and contract conversion +- **THEN** it returns a robot policy action with action-space identity, numeric values, and metadata rather than a runtime sidecar action frame + +#### Scenario: Policy module publishes runtime-independent action chunk +- **WHEN** a robot policy module completes live chunk inference and contract conversion +- **THEN** it publishes a robot policy action chunk with action-space identity, ordered numeric action values, and metadata rather than controller-ready joint commands or runtime action frames + +### Requirement: Live policy observation stream +The system SHALL allow a robot policy module to receive live robot policy observations through a stream and retain the latest ready observation for asynchronously triggered chunk inference. + +#### Scenario: Module stores latest live observation +- **WHEN** a robot policy observation arrives on the live policy observation stream +- **THEN** the robot policy module records it as the latest candidate observation for future chunk inference triggers + +#### Scenario: Trigger fails clearly without observation +- **WHEN** a live chunk inference trigger arrives before any ready robot policy observation has been received +- **THEN** the robot policy module reports a clear not-ready result and does not run backend inference + +### Requirement: Fast live chunk inference trigger +The system SHALL expose a fast trigger on the robot policy module that requests asynchronous chunk inference from the latest live observation without returning the chunk as the trigger response. + +#### Scenario: Trigger starts asynchronous inference +- **WHEN** the robot policy module receives a live chunk inference trigger and has a latest ready observation +- **THEN** it starts or schedules backend chunk inference and returns before the chunk result is available + +#### Scenario: Chunk result returns on stream +- **WHEN** asynchronous chunk inference completes successfully +- **THEN** the robot policy module publishes the resulting robot policy action chunk on its chunk output stream + +#### Scenario: Duplicate trigger does not start concurrent backend inference +- **WHEN** a trigger arrives while chunk inference is already in flight for the same module instance +- **THEN** the robot policy module does not start an unbounded concurrent backend inference request diff --git a/openspec/specs/runtime-libero-pro-sidecar/spec.md b/openspec/specs/runtime-libero-pro-sidecar/spec.md index ca789baa03..cebbb270b2 100644 --- a/openspec/specs/runtime-libero-pro-sidecar/spec.md +++ b/openspec/specs/runtime-libero-pro-sidecar/spec.md @@ -1,9 +1,7 @@ ## Purpose Define the LIBERO-PRO runtime package, registered-suite task selection, asset preparation boundary, motor-control contract, observation export, scoring, and verification split for LIBERO-PRO benchmark demos through a Simulator Runtime Module. - ## Requirements - ### Requirement: LIBERO-PRO sidecar package The system SHALL evolve the first-class LIBERO-PRO runtime package into an import-safe package that provides a Simulator Runtime Module for LIBERO-PRO execution and removes the existing HTTP server boundary as part of migration success. @@ -23,7 +21,7 @@ The system SHALL evolve the first-class LIBERO-PRO runtime package into an impor The LIBERO-PRO runtime package SHALL support registered LIBERO-PRO benchmark suites in v1 using backend options for benchmark name, task order index, task index, init-state index, controller, cameras, horizon, and asset roots. #### Scenario: Registered task is described -- **WHEN** the sidecar is configured with a registered LIBERO-PRO benchmark name, task order index, task index, and init-state index +- **WHEN** the runtime module is configured with a registered LIBERO-PRO benchmark name, task order index, task index, and init-state index - **THEN** the runtime description includes task metadata such as benchmark name, task name, language, BDDL path, init-state index, controller, horizon, and camera configuration #### Scenario: Dynamic perturbation request is rejected @@ -49,11 +47,11 @@ The system SHALL support explicit opt-in LIBERO-PRO runtime asset bootstrap whil The LIBERO-PRO runtime package SHALL expose the full-control v1 path only when the selected task and controller provide a Panda joint-position plus gripper whole-body motor surface compatible with DimOS motor action frames. #### Scenario: Compatible motor surface is described -- **WHEN** the selected LIBERO-PRO environment exposes the expected Panda joint-position plus gripper action surface +- **WHEN** the selected LIBERO-PRO environment exposes the expected Panda joint-position plus gripper action surface for motor-frame mode - **THEN** the runtime description reports a stable ordered motor surface with supported position command mode and the expected motor count -#### Scenario: Incompatible controller fails fast -- **WHEN** the selected LIBERO-PRO environment exposes only OSC pose control or an action dimension that cannot be mapped to Panda joint-position plus gripper commands +#### Scenario: Incompatible motor mode fails fast +- **WHEN** motor-frame mode is selected but the LIBERO environment exposes only a native end-effector action surface or an action dimension that cannot be mapped to Panda joint-position plus gripper commands - **THEN** the runtime module rejects the episode setup with a clear protocol error before accepting step requests ### Requirement: LIBERO-PRO step ownership and observation export @@ -88,3 +86,33 @@ The system SHALL verify LIBERO-PRO module-runtime behavior with always-on contra #### Scenario: Manual integration exercises real LIBERO-PRO - **WHEN** a developer runs the optional real LIBERO-PRO integration with prepared dependencies and assets - **THEN** it launches the placed Simulator Runtime Module, runs reset and synchronous step RPCs for one registered task, observes camera stream publication, and writes score and artifacts + +### Requirement: Native LIBERO action mode +The LIBERO runtime module SHALL support a native LIBERO action mode that follows the official LeRobot LIBERO action setup for relative end-effector delta plus gripper actions. + +#### Scenario: Native action mode validates environment action spec +- **WHEN** the runtime module starts in native LIBERO action mode +- **THEN** it inspects the LIBERO environment action spec and requires action dimension `(7,)` with bounds compatible with `[-1, 1]` + +#### Scenario: Native action mode is described +- **WHEN** the runtime module description is requested in native LIBERO action mode +- **THEN** it reports the native action surface identifier, action shape, action bounds, action mode metadata, task metadata, language, horizon, and camera configuration + +#### Scenario: Native action mode accepts runtime action frame +- **WHEN** DimOS sends a runtime action frame with `space_id` `libero.ee_delta_6d_gripper.normalized.v1` and valid `float32[7]` values +- **THEN** the runtime module maps the values directly to the LIBERO environment step action and returns observations, reward, done, and success metadata + +#### Scenario: Native action mode rejects motor frame +- **WHEN** the runtime module is running in native LIBERO action mode and receives a motor action frame +- **THEN** it rejects the step request with a clear protocol error + +### Requirement: Native LIBERO observation export for policy rollout +The LIBERO runtime module SHALL export the observations needed by the VLA-JEPA LIBERO policy contract when running native LIBERO action mode through DimOS streams and runtime metadata. + +#### Scenario: Policy observations are available after reset +- **WHEN** the runtime module resets a registered task in native LIBERO action mode +- **THEN** DimOS streams and runtime metadata include agent-view camera observation metadata, wrist or eye-in-hand camera observation metadata when available, robot state observation metadata, and task language metadata for contract conversion + +#### Scenario: Policy observations are available after step +- **WHEN** the runtime module completes a native runtime action step +- **THEN** DimOS streams and runtime metadata include updated camera and robot state observations needed for the next policy inference tick diff --git a/openspec/specs/runtime-protocol/spec.md b/openspec/specs/runtime-protocol/spec.md index 85106dd58a..fc774e5914 100644 --- a/openspec/specs/runtime-protocol/spec.md +++ b/openspec/specs/runtime-protocol/spec.md @@ -1,9 +1,7 @@ ## Purpose Define the lightweight backend-neutral protocol contract shared by DimOS and simulator runtime packages. - ## Requirements - ### Requirement: Shared runtime protocol package The system SHALL provide lightweight backend-neutral runtime protocol models that can be reused by DimOS and simulator runtime packages without requiring HTTP transport, the main DimOS package in schema-only contexts, or any simulator backend SDK. @@ -58,3 +56,29 @@ Runtime protocol models MUST NOT expose Robosuite, LIBERO-PRO, OmniGibson, DimOS #### Scenario: Robosuite observation is translated - **WHEN** Robosuite produces an `OrderedDict` observation - **THEN** the Robosuite sidecar translates it into runtime protocol observation and motor state frames before sending it to DimOS + +### Requirement: Native runtime action frames +The runtime protocol SHALL define a native runtime action frame for benchmark action surfaces that are not DimOS motor or joint command surfaces. + +#### Scenario: Runtime action frame identifies semantic action surface +- **WHEN** a runtime action frame is serialized +- **THEN** it includes a discriminator, semantic action surface identifier, numeric action values, and sequence or tick identity without requiring motor names, motor command modes, gains, or joint position fields + +#### Scenario: Runtime action frame validates numeric action values +- **WHEN** a runtime action frame contains non-finite values or values that cannot be parsed as a numeric vector +- **THEN** protocol validation rejects the frame before backend-specific step logic runs + +### Requirement: Step request action frame union +The runtime protocol SHALL allow a step request to carry either a motor action frame or a native runtime action frame while preserving explicit frame discrimination. + +#### Scenario: Motor step request remains valid +- **WHEN** an existing client sends a step request with a valid motor action frame +- **THEN** protocol validation accepts the request as a motor-frame step request + +#### Scenario: Native runtime step request is valid +- **WHEN** a client sends a step request with a valid native runtime action frame +- **THEN** protocol validation accepts the request as a runtime-action step request + +#### Scenario: Ambiguous action frame is rejected +- **WHEN** a step request action lacks a supported discriminator or mixes incompatible motor-frame and runtime-action-frame fields +- **THEN** protocol validation rejects the request before it reaches sidecar step logic diff --git a/openspec/specs/runtime-scripted-demos/spec.md b/openspec/specs/runtime-scripted-demos/spec.md index f258585a10..70f171a0db 100644 --- a/openspec/specs/runtime-scripted-demos/spec.md +++ b/openspec/specs/runtime-scripted-demos/spec.md @@ -85,3 +85,53 @@ The scripted demos SHALL be launched through plain scripts and MUST NOT require #### Scenario: Developer runs demo script directly - **WHEN** a developer invokes the demo script with a config path - **THEN** the script performs orchestration directly rather than delegating to a new DimOS CLI subcommand + +### Requirement: LeRobot LIBERO policy rollout demo +The system SHALL include a module-backed LeRobot LIBERO policy rollout demo that validates policy loading, contract conversion, native runtime actions, placed runtime-module stepping, stream snapshot collection, score collection, artifact output, and teardown through the same module-backed policy evaluation path used by DimOS workflow. + +#### Scenario: Policy rollout demo starts native runtime module and policy rollout stack +- **WHEN** a developer runs the LeRobot LIBERO policy rollout demo with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the demo starts the placed LIBERO runtime module in native LIBERO action mode, initializes module-backed benchmark evaluation with a robot policy module, LeRobot backend, and VLA-JEPA LIBERO contract, runs the configured episode matrix, writes artifacts, and tears down all resources + +#### Scenario: Policy rollout demo bypasses ControlCoordinator +- **WHEN** the LeRobot LIBERO policy rollout demo executes policy actions +- **THEN** actions flow from the robot policy module through benchmark evaluation to the runtime module as native runtime action frames without using ControlCoordinator, JointTrajectoryTask, EndEffectorDeltaTrajectoryTask, the SHM motor bridge, or motor action frames + +#### Scenario: Policy rollout demo consumes runtime streams directly +- **WHEN** the runtime module publishes configured camera images and robot-state events during reset or step +- **THEN** the policy rollout demo captures those DimOS stream outputs as the source of policy observations and optional videos without constructing HTTP payload references + +#### Scenario: Policy rollout demo enforces success gate +- **WHEN** the 50-episode policy rollout gate completes without setup or contract aborts +- **THEN** the demo passes only if the recorded success rate is greater than `0.50` + +#### Scenario: Policy rollout demo records required artifacts +- **WHEN** the LeRobot LIBERO policy rollout demo runs +- **THEN** it writes rollout summary, per-episode JSONL records, runtime description, checkpoint metadata, logs, and cleanup status to the artifact directory + +#### Scenario: Existing scripted demos remain unchanged +- **WHEN** the fake, Robosuite, or existing LIBERO-PRO motor demos are run +- **THEN** they continue to validate scripted runtime plumbing without requiring LeRobot policy dependencies or policy task success + +### Requirement: LeRobot LIBERO live policy stream parity gate +The system SHALL include a script-based LeRobot LIBERO live policy stream parity gate that validates the real VLA-JEPA policy through module-native RobotPolicyModule live chunk inference, ControlCoordinator policy chunk execution, LIBERO runtime observation streams, score collection, artifact output, and teardown. + +#### Scenario: Live gate runs the real policy +- **WHEN** a developer runs the LeRobot LIBERO live policy stream parity gate with compatible LeRobot dependencies and prepared LIBERO assets +- **THEN** the gate loads the actual `lerobot/VLA-JEPA-LIBERO` policy rather than using fake or fixed policy actions as the acceptance path + +#### Scenario: Live gate routes policy through ControlCoordinator +- **WHEN** the live parity gate executes policy actions +- **THEN** observations flow into RobotPolicyModule, inferred robot policy action chunks flow through ControlCoordinator, and the policy chunk control task drives the runtime control path rather than benchmark evaluation adapting actions directly into native runtime `step()` calls + +#### Scenario: Live gate uses module-native wiring +- **WHEN** the live parity gate wires the runtime observation stream, policy module, and coordinator +- **THEN** `RobotPolicyModule.policy_action_chunk` is connected to `ControlCoordinator.robot_policy_action_chunk` through a module stream boundary, and the coordinator requests refills through the policy module fast trigger RPC rather than through direct local callback execution + +#### Scenario: Live gate enforces 10-episode real-policy success +- **WHEN** the 10-episode live parity gate over `libero_object` completes without setup or contract aborts +- **THEN** the gate passes only if the recorded success rate is greater than `0.50` + +#### Scenario: Live gate records chunk diagnostics +- **WHEN** the LeRobot LIBERO live parity gate runs +- **THEN** it writes rollout artifacts that include policy success metrics and live-path diagnostics such as chunk counts, refill triggers, consumed actions, stale deactivations, and cleanup status diff --git a/packages/dimos-libero-pro-sidecar/pyproject.toml b/packages/dimos-libero-pro-sidecar/pyproject.toml index 487237c9fb..f7c200b565 100644 --- a/packages/dimos-libero-pro-sidecar/pyproject.toml +++ b/packages/dimos-libero-pro-sidecar/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "dimos", "dimos-runtime-protocol", "libero", + "mujoco<3.3", ] [tool.uv.sources] diff --git a/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/blueprint.py b/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/blueprint.py index 2400a2f5ba..bdc75d5f51 100644 --- a/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/blueprint.py +++ b/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/blueprint.py @@ -21,6 +21,7 @@ from dimos.core.coordination.blueprints import Blueprint, autoconnect from dimos.core.runtime_environment import PythonProjectRuntimeEnvironment from dimos_libero_pro_sidecar.module import LiberoProRuntimeModule +from dimos_libero_pro_sidecar.server import ActionMode LIBERO_PRO_RUNTIME_ENV_NAME = "dimos-libero-pro-runtime" LIBERO_PRO_RUNTIME_PROJECT = Path(__file__).resolve().parents[2] @@ -36,8 +37,11 @@ def libero_pro_runtime_blueprint( task_order_index: int = 0, task_index: int = 0, init_state_index: int = 0, + action_mode: ActionMode = "motor", controller: str = "JOINT_POSITION", camera_names: tuple[str, ...] = ("agentview",), + camera_height: int = 128, + camera_width: int = 128, control_freq: int = 20, horizon: int = 1000, seed: int | None = None, @@ -60,8 +64,11 @@ def libero_pro_runtime_blueprint( task_order_index=task_order_index, task_index=task_index, init_state_index=init_state_index, + action_mode=action_mode, controller=controller, camera_names=camera_names, + camera_height=camera_height, + camera_width=camera_width, control_freq=control_freq, horizon=horizon, seed=seed, diff --git a/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/module.py b/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/module.py index 804e263695..5d8f97caae 100644 --- a/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/module.py +++ b/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/module.py @@ -16,6 +16,7 @@ from __future__ import annotations +from io import BytesIO from pathlib import Path from dimos_runtime_protocol.models import ( @@ -41,7 +42,11 @@ module_runtime_description, publish_output, ) -from dimos_libero_pro_sidecar.server import LiberoProRuntimeConfig, LiberoProRuntimeState +from dimos_libero_pro_sidecar.server import ( + ActionMode, + LiberoProRuntimeConfig, + LiberoProRuntimeState, +) class LiberoProRuntimeModuleConfig(ModuleConfig): @@ -52,8 +57,11 @@ class LiberoProRuntimeModuleConfig(ModuleConfig): task_order_index: int = 0 task_index: int = 0 init_state_index: int = 0 + action_mode: ActionMode = "motor" controller: str = "JOINT_POSITION" camera_names: tuple[str, ...] = ("agentview",) + camera_height: int = 128 + camera_width: int = 128 control_freq: int = 20 horizon: int = 1000 seed: int | None = None @@ -80,8 +88,11 @@ def __init__( task_order_index: int = 0, task_index: int = 0, init_state_index: int = 0, + action_mode: ActionMode = "motor", controller: str = "JOINT_POSITION", camera_names: tuple[str, ...] = ("agentview",), + camera_height: int = 128, + camera_width: int = 128, control_freq: int = 20, horizon: int = 1000, seed: int | None = None, @@ -97,8 +108,11 @@ def __init__( task_order_index=task_order_index, task_index=task_index, init_state_index=init_state_index, + action_mode=action_mode, controller=controller, camera_names=camera_names, + camera_height=camera_height, + camera_width=camera_width, control_freq=control_freq, horizon=horizon, seed=seed, @@ -123,8 +137,11 @@ def __init__( task_order_index=task_order_index, task_index=task_index, init_state_index=init_state_index, + action_mode=action_mode, controller=controller, camera_names=tuple(camera_names), + camera_height=camera_height, + camera_width=camera_width, control_freq=control_freq, horizon=horizon, seed=seed, @@ -133,6 +150,8 @@ def __init__( ) self._state: LiberoProRuntimeState | None = None self._runtime_stopped = False + self._latest_snapshot_observations: list[ObservationFrame] = [] + self._latest_snapshot_values: dict[str, np.ndarray] = {} @rpc def stop(self) -> None: @@ -190,6 +209,17 @@ def score(self) -> ScoreOutput: return self._owner.call(lambda: self._state_or_create().score()) + @rpc + def observation_snapshot(self) -> tuple[list[ObservationFrame], dict[str, np.ndarray]]: + """Return latest observation metadata and arrays published by this module.""" + + return self._owner.call( + lambda: ( + list(self._latest_snapshot_observations), + dict(self._latest_snapshot_values), + ) + ) + def _state_or_create(self) -> LiberoProRuntimeState: if self._state is None: self._state = LiberoProRuntimeState(self._runtime_config) @@ -198,18 +228,24 @@ def _state_or_create(self) -> LiberoProRuntimeState: def _publish_observation_outputs( self, observations: list[ObservationFrame], event: str ) -> None: + snapshot_observations: list[ObservationFrame] = [] + snapshot_values: dict[str, np.ndarray] = {} + state = self._state_or_create() for observation in observations: if observation.kind == ObservationKind.STATE: publish_output(self.runtime_event, observation) - state = self._state_or_create() - for camera_name in state.config.camera_names: - image = state._last_obs.get(f"{camera_name}_image") - if image is None: + snapshot_observations.append(observation) + continue + if observation.kind != ObservationKind.IMAGE: continue - array = np.asarray(image) + array = self._snapshot_image_value(state, observation) + if array is None: + continue + snapshot_values[observation.stream] = array + snapshot_observations.append(observation) publish_output( self.color_image, - Image(data=array, format=ImageFormat.RGB, frame_id=camera_name), + Image(data=array, format=ImageFormat.RGB, frame_id=observation.stream), ) height, width = array.shape[:2] publish_output( @@ -218,15 +254,27 @@ def _publish_observation_outputs( fov_deg=45.0, width=width, height=height, - frame_id=camera_name, + frame_id=observation.stream, ), ) - publish_output( - self.runtime_event, - ObservationFrame( - stream="runtime_event", - kind=ObservationKind.TEXT, - inline_text=event, - metadata={"sequence": state._sequence}, - ), + event_observation = ObservationFrame( + stream="runtime_event", + kind=ObservationKind.TEXT, + inline_text=event, + metadata={"sequence": state._sequence}, ) + snapshot_observations.append(event_observation) + self._latest_snapshot_observations = snapshot_observations + self._latest_snapshot_values = snapshot_values + publish_output(self.runtime_event, event_observation) + + def _snapshot_image_value( + self, state: LiberoProRuntimeState, observation: ObservationFrame + ) -> np.ndarray | None: + if observation.data_ref is not None: + payload_id = observation.data_ref.removeprefix("/payloads/") + return np.asarray(np.load(BytesIO(state.payload_bytes(payload_id)), allow_pickle=False)) + image = state._last_obs.get(f"{observation.stream}_image") + if image is None: + return None + return np.asarray(image) diff --git a/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/server.py b/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/server.py index 5e66208d00..319c07046b 100644 --- a/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/server.py +++ b/packages/dimos-libero-pro-sidecar/src/dimos_libero_pro_sidecar/server.py @@ -18,26 +18,36 @@ from collections.abc import Sequence from dataclasses import dataclass +import importlib.util from io import BytesIO +import os from pathlib import Path import time -from typing import Protocol, cast +from typing import Literal, Protocol, cast from dimos_runtime_protocol import ( CommandMode, EpisodeResetRequest, EpisodeResetResponse, + MotorActionFrame, MotorDescription, MotorStateFrame, ObservationFrame, ObservationKind, RobotMotorSurface, + RuntimeActionFrame, RuntimeDescription, ScoreOutput, StepRequest, StepResponse, ) +ActionMode = Literal["motor", "native", "native_absolute"] +NATIVE_ACTION_SPACE_ID = "libero.ee_delta_6d_gripper.normalized.v1" +NATIVE_ABSOLUTE_ACTION_SPACE_ID = "libero.ee_pose_axis_angle_gripper.absolute.v1" +NATIVE_LIBERO_CONTROLLER = "OSC_POSE" +NATIVE_RESET_SETTLE_STEPS = 10 + def require_libero(*, visualize: bool = False) -> tuple[object, LiberoEnvFactory]: """Import LIBERO-PRO dependencies only on the runtime path.""" @@ -84,8 +94,11 @@ class LiberoProRuntimeConfig: task_order_index: int = 0 task_index: int = 0 init_state_index: int = 0 + action_mode: ActionMode = "motor" controller: str = "JOINT_POSITION" camera_names: tuple[str, ...] = ("agentview",) + camera_height: int = 128 + camera_width: int = 128 control_freq: int = 20 horizon: int = 1000 seed: int | None = None @@ -139,6 +152,7 @@ def step( class RealLiberoBackend: def __init__(self, config: LiberoProRuntimeConfig) -> None: + ensure_libero_config(config.bddl_root, config.init_states_root) libero_benchmark, env_cls = require_libero(visualize=config.visualize) benchmark_factory = libero_benchmark.get_benchmark(config.benchmark_name) benchmark = benchmark_factory(config.task_order_index) @@ -148,38 +162,50 @@ def __init__(self, config: LiberoProRuntimeConfig) -> None: bddl_file = _task_bddl_file(config, benchmark, task) init_states = _load_init_states(config, benchmark, task) self._init_states = init_states + self._action_mode = config.action_mode self._env: LiberoEnv = env_cls( bddl_file_name=str(bddl_file), robots=["Panda"], use_camera_obs=True, has_renderer=config.visualize, has_offscreen_renderer=True, - camera_heights=128, - camera_widths=128, + camera_heights=config.camera_height, + camera_widths=config.camera_width, camera_names=list(config.camera_names), - controller=config.controller, + controller=_env_controller(config), control_freq=config.control_freq, - horizon=config.horizon, + horizon=config.horizon + _reset_settle_steps(config), render_camera=config.camera_names[0] if config.camera_names else None, ) + if config.action_mode == "native": + _set_controller_use_delta(self._env) + elif config.action_mode == "native_absolute": + _set_controller_use_delta(self._env, use_delta=False) self.action_low, self.action_high = _action_bounds(self._env) def reset(self, init_state_index: int) -> dict[str, object]: obs = self._env.reset() set_init_state = self._env.set_init_state obs = set_init_state(self._init_states[init_state_index]) - return cast("dict[str, object]", obs) + if self._action_mode == "native": + for _ in range(NATIVE_RESET_SETTLE_STEPS): + obs, _, _, _ = self._env.step([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0]) + _set_controller_use_delta(self._env) + elif self._action_mode == "native_absolute": + _set_controller_use_delta(self._env, use_delta=False) + return _with_controller_state(cast("dict[str, object]", obs), self._env) def step( self, action: Sequence[float] ) -> tuple[dict[str, object], float, bool, dict[str, object]]: obs, reward, done, info = self._env.step(action) + obs = _with_controller_state(cast("dict[str, object]", obs), self._env) typed_info = cast("dict[str, object]", info) if not any(key in typed_info for key in ("success", "is_success", "task_success")): check_success = getattr(self._env, "check_success", None) if callable(check_success): typed_info["success"] = bool(check_success()) - return cast("dict[str, object]", obs), float(reward), bool(done), typed_info + return obs, float(reward), bool(done), typed_info def render(self) -> None: render = getattr(self._env, "render", None) @@ -216,10 +242,19 @@ def __init__( self._validate_action_surface() def _validate_action_surface(self) -> None: + if self.config.action_mode == "native": + self._validate_native_delta_action_surface() + return + if self.config.action_mode == "native_absolute": + self._validate_native_absolute_action_surface() + return + if self.config.action_mode != "motor": + raise RuntimeError(f"unsupported LIBERO-PRO action mode: {self.config.action_mode}") + self._validate_motor_action_surface() + + def _validate_motor_action_surface(self) -> None: if self.config.controller not in {"JOINT_POSITION", "PANDA_JOINT_POSITION"}: - raise RuntimeError( - f"unsupported LIBERO-PRO controller profile: {self.config.controller}" - ) + raise RuntimeError(f"unsupported LIBERO-PRO controller: {self.config.controller}") if len(self._action_low) != len(self.motor_names) or len(self._action_high) != len( self.motor_names ): @@ -228,11 +263,39 @@ def _validate_action_surface(self) -> None: f"action_dim={len(self._action_low)} motors={len(self.motor_names)}" ) + def _validate_native_delta_action_surface(self) -> None: + if len(self._action_low) != 7 or len(self._action_high) != 7: + raise RuntimeError( + f"native LIBERO action mode expects action_dim=7, got {len(self._action_low)}" + ) + incompatible = [ + (low, high) + for low, high in zip(self._action_low, self._action_high, strict=True) + if low > -1.0 or high < 1.0 + ] + if incompatible: + raise RuntimeError( + "native LIBERO action mode requires action_spec bounds compatible with [-1, 1]" + ) + + def _validate_native_absolute_action_surface(self) -> None: + if len(self._action_low) != 7 or len(self._action_high) != 7: + raise RuntimeError( + "absolute native LIBERO action mode expects action_dim=7, " + f"got {len(self._action_low)}" + ) + def describe(self) -> RuntimeDescription: return RuntimeDescription( runtime_id="libero-pro", backend="libero-pro", - capabilities=["sync-http", "whole-body-motor-position", "libero-pro"], + capabilities=[ + "sync-http", + "libero-pro", + "whole-body-motor-position" + if self.config.action_mode == "motor" + else "runtime-action", + ], robot_surfaces=[ RobotMotorSurface( robot_id=self.config.robot_id, @@ -254,11 +317,29 @@ def describe(self) -> RuntimeDescription: "init_states_root": str(self.config.init_states_root), "init_state_index": self.config.init_state_index, "controller": self.config.controller, + "effective_controller": _env_controller(self.config), + "effective_horizon": self.config.horizon + _reset_settle_steps(self.config), + "reset_settle_steps": _reset_settle_steps(self.config), + "action_mode": self.config.action_mode, "horizon": self.config.horizon, "visualize": self.config.visualize, "camera_names": list(self.config.camera_names), + "camera_config": { + "names": list(self.config.camera_names), + "height": self.config.camera_height, + "width": self.config.camera_width, + }, "action_low": self._action_low, "action_high": self._action_high, + "action_shape": [len(self._action_low)], + "task_metadata": { + "benchmark_name": self.config.benchmark_name, + "task_order_index": self.config.task_order_index, + "task_index": self.config.task_index, + "task_name": self._backend.task_name, + "init_state_index": self.config.init_state_index, + }, + "native_action_space_id": _native_action_space_id(self.config.action_mode), }, ) @@ -278,22 +359,10 @@ def reset(self, request: EpisodeResetRequest) -> EpisodeResetResponse: ) def step(self, request: StepRequest) -> StepResponse: - if request.action.robot_id != self.config.robot_id: - raise ValueError(f"unexpected robot id {request.action.robot_id!r}") - if request.action.names != self.motor_names: - raise ValueError("action motor names do not match runtime surface") - if request.action.mode != CommandMode.POSITION: - raise ValueError(f"unsupported command mode {request.action.mode}") - if len(request.action.q) != len(self.motor_names): - raise ValueError( - f"expected {len(self.motor_names)} q targets, got {len(request.action.q)}" - ) - action = [ - min(max(float(v), low), high) - for v, low, high in zip( - request.action.q, self._action_low, self._action_high, strict=True - ) - ] + if self.config.action_mode in {"native", "native_absolute"}: + action = self._native_step_action(request.action) + else: + action = self._motor_step_action(request.action) obs, reward, done, info = self._backend.step(action) self._sequence += 1 self._last_obs = obs @@ -313,6 +382,40 @@ def step(self, request: StepRequest) -> StepResponse: info={"backend_sequence": self._sequence}, ) + def _motor_step_action( + self, action_frame: MotorActionFrame | RuntimeActionFrame + ) -> list[float]: + if isinstance(action_frame, RuntimeActionFrame): + raise ValueError("motor action mode requires MotorActionFrame, got RuntimeActionFrame") + if action_frame.robot_id != self.config.robot_id: + raise ValueError(f"unexpected robot id {action_frame.robot_id!r}") + if action_frame.names != self.motor_names: + raise ValueError("action motor names do not match runtime surface") + if action_frame.mode != CommandMode.POSITION: + raise ValueError(f"unsupported command mode {action_frame.mode}") + if len(action_frame.q) != len(self.motor_names): + raise ValueError( + f"expected {len(self.motor_names)} q targets, got {len(action_frame.q)}" + ) + return [ + min(max(float(v), low), high) + for v, low, high in zip( + action_frame.q, self._action_low, self._action_high, strict=True + ) + ] + + def _native_step_action( + self, action_frame: MotorActionFrame | RuntimeActionFrame + ) -> list[float]: + if isinstance(action_frame, MotorActionFrame): + raise ValueError("native action mode requires RuntimeActionFrame, got MotorActionFrame") + expected_space_id = _native_action_space_id(self.config.action_mode) + if action_frame.space_id != expected_space_id: + raise ValueError(f"unexpected runtime action space_id {action_frame.space_id!r}") + if len(action_frame.values) != 7: + raise ValueError(f"expected 7 runtime action values, got {len(action_frame.values)}") + return [float(value) for value in action_frame.values] + def _render_if_enabled(self) -> None: if not self.config.visualize: return @@ -371,7 +474,11 @@ def _observations(self, tick_id: int) -> list[ObservationFrame]: stream="robot_state", kind=ObservationKind.STATE, inline_text=f"tick={tick_id} reward={self._last_reward}", - metadata={"sequence": self._sequence}, + metadata={ + "sequence": self._sequence, + "state": self._policy_robot_state(), + "xvla_robot_state": self._xvla_policy_robot_state(), + }, ) ] for camera_name in self.config.camera_names: @@ -399,6 +506,23 @@ def _observations(self, tick_id: int) -> list[ObservationFrame]: ) return frames + def _policy_robot_state(self) -> list[float]: + eef_pos = _padded(_float_list(self._last_obs.get("robot0_eef_pos", [])), 3) + eef_axis_angle = _quat_to_axis_angle( + _padded(_float_list(self._last_obs.get("robot0_eef_quat", [])), 4, fill=0.0) + ) + gripper_qpos = _padded(_float_list(self._last_obs.get("robot0_gripper_qpos", [])), 2) + return [*eef_pos, *eef_axis_angle, *gripper_qpos] + + def _xvla_policy_robot_state(self) -> dict[str, object]: + eef_pos = _padded(_float_list(self._last_obs.get("robot0_eef_pos", [])), 3) + eef_mat = _matrix3(self._last_obs.get("robot0_eef_mat")) + if eef_mat is None: + eef_mat = _quat_to_matrix( + _padded(_float_list(self._last_obs.get("robot0_eef_quat", [])), 4, fill=0.0) + ) + return {"eef.pos": [eef_pos], "eef.mat": [eef_mat]} + def _store_payload(self, stream: str, tick_id: int, value: object) -> tuple[str, bytes]: payload_id = f"{stream}-{tick_id:06d}-{self._sequence:06d}.npy" payload = _npy_bytes(value) @@ -425,8 +549,6 @@ def validate_assets(config: LiberoProRuntimeConfig) -> None: def bootstrap_assets(config: LiberoProRuntimeConfig) -> None: - import os - repo_id = os.environ.get("LIBERO_PRO_HF_REPO_ID") if not repo_id: raise RuntimeError( @@ -447,6 +569,72 @@ def bootstrap_assets(config: LiberoProRuntimeConfig) -> None: ) +def discover_libero_asset_roots() -> tuple[Path, Path] | None: + """Return standard LIBERO package bddl/init roots when LIBERO is installed.""" + + spec = importlib.util.find_spec("libero.libero") + if spec is None: + return None + locations = spec.submodule_search_locations + if locations: + package_root = Path(next(iter(locations))) + elif spec.origin: + package_root = Path(spec.origin).parent + else: + return None + bddl_root = package_root / "bddl_files" + init_states_root = package_root / "init_files" + if bddl_root.exists() and init_states_root.exists(): + return bddl_root, init_states_root + return None + + +def ensure_libero_config(bddl_root: Path, init_states_root: Path) -> None: + """Create LIBERO's config.yaml noninteractively when it is missing. + + The upstream LIBERO package prompts on first import if the config file is + absent. Sidecar runs must be noninteractive, so we create the same default + shape using the roots selected by the benchmark runner or CLI. + """ + + config_root = Path(os.environ.get("LIBERO_CONFIG_PATH", Path.home() / ".libero")) + config_file = config_root / "config.yaml" + if config_file.exists(): + return + benchmark_root = bddl_root.parent + config_root.mkdir(parents=True, exist_ok=True) + config_file.write_text( + "\n".join( + [ + f"assets: {benchmark_root / 'assets'}", + f"bddl_files: {bddl_root}", + f"benchmark_root: {benchmark_root}", + f"datasets: {benchmark_root.parent / 'datasets'}", + f"init_states: {init_states_root}", + "", + ] + ) + ) + + +def _resolve_asset_roots(bddl_root: Path | None, init_states_root: Path | None) -> tuple[Path, Path]: + if bddl_root is not None and init_states_root is not None: + return bddl_root, init_states_root + discovered = discover_libero_asset_roots() + if discovered is None: + missing = [] + if bddl_root is None: + missing.append("--bddl-root") + if init_states_root is None: + missing.append("--init-states-root") + raise SystemExit( + "LIBERO asset roots were not provided and could not be discovered from an " + f"installed libero package; pass {' and '.join(missing)} or install libero" + ) + discovered_bddl, discovered_init = discovered + return bddl_root or discovered_bddl, init_states_root or discovered_init + + def _task_bddl_file(config: LiberoProRuntimeConfig, benchmark: object, task: object) -> Path: get_path = getattr(benchmark, "get_task_bddl_file_path", None) if callable(get_path): @@ -459,6 +647,48 @@ def _task_bddl_file(config: LiberoProRuntimeConfig, benchmark: object, task: obj return config.bddl_root / f"{getattr(task, 'name', f'task_{config.task_index}')}.bddl" +def _env_controller(config: LiberoProRuntimeConfig) -> str: + if config.action_mode in {"native", "native_absolute"}: + return NATIVE_LIBERO_CONTROLLER + return config.controller + + +def _reset_settle_steps(config: LiberoProRuntimeConfig) -> int: + return NATIVE_RESET_SETTLE_STEPS if config.action_mode == "native" else 0 + + +def _native_action_space_id(action_mode: ActionMode) -> str: + if action_mode == "native_absolute": + return NATIVE_ABSOLUTE_ACTION_SPACE_ID + return NATIVE_ACTION_SPACE_ID + + +def _set_controller_use_delta(env: object, *, use_delta: bool = True) -> None: + for robot in getattr(env, "robots", ()) or (): + controller = getattr(robot, "controller", None) + if controller is not None and hasattr(controller, "use_delta"): + controller.use_delta = use_delta + + +def _with_controller_state(obs: dict[str, object], env: object) -> dict[str, object]: + enriched = dict(obs) + matrix = _controller_eef_matrix(env) + if matrix is not None: + enriched["robot0_eef_mat"] = matrix + return enriched + + +def _controller_eef_matrix(env: object) -> list[list[float]] | None: + for robot in getattr(env, "robots", ()) or (): + controller = getattr(robot, "controller", None) + if controller is None: + continue + matrix = _matrix3(getattr(controller, "ee_ori_mat", None)) + if matrix is not None: + return matrix + return None + + def _load_init_states( config: LiberoProRuntimeConfig, benchmark: object, task: object ) -> Sequence[object]: @@ -527,6 +757,59 @@ def _mean_or_zero(values: Sequence[float]) -> float: return float(sum(values) / len(values)) if values else 0.0 +def _padded(values: Sequence[float], size: int, *, fill: float = 0.0) -> list[float]: + return [*values, *([fill] * size)][:size] + + +def _quat_to_axis_angle(quat: Sequence[float]) -> list[float]: + import math + + if len(quat) != 4 or not any(quat): + return [0.0, 0.0, 0.0] + x, y, z, w = quat + norm = math.sqrt(x * x + y * y + z * z + w * w) + if norm == 0.0: + return [0.0, 0.0, 0.0] + x, y, z, w = x / norm, y / norm, z / norm, w / norm + w = min(1.0, max(-1.0, w)) + angle = 2.0 * math.acos(w) + scale = math.sqrt(max(0.0, 1.0 - w * w)) + if scale < 1e-8: + return [0.0, 0.0, 0.0] + return [x / scale * angle, y / scale * angle, z / scale * angle] + + +def _quat_to_matrix(quat: Sequence[float]) -> list[list[float]]: + if len(quat) != 4 or not any(quat): + return _identity_matrix3() + x, y, z, w = (float(item) for item in quat) + norm = (x * x + y * y + z * z + w * w) ** 0.5 + if norm == 0.0: + return _identity_matrix3() + x, y, z, w = x / norm, y / norm, z / norm, w / norm + return [ + [1.0 - 2.0 * (y * y + z * z), 2.0 * (x * y - z * w), 2.0 * (x * z + y * w)], + [2.0 * (x * y + z * w), 1.0 - 2.0 * (x * x + z * z), 2.0 * (y * z - x * w)], + [2.0 * (x * z - y * w), 2.0 * (y * z + x * w), 1.0 - 2.0 * (x * x + y * y)], + ] + + +def _matrix3(value: object) -> list[list[float]] | None: + rows = getattr(value, "tolist", lambda: value)() + if not isinstance(rows, Sequence) or isinstance(rows, (str, bytes)) or len(rows) != 3: + return None + matrix: list[list[float]] = [] + for row in rows: + if not isinstance(row, Sequence) or isinstance(row, (str, bytes)) or len(row) != 3: + return None + matrix.append([float(item) for item in row]) + return matrix + + +def _identity_matrix3() -> list[list[float]]: + return [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] + + def _shape_list(value: object) -> list[int]: shape = getattr(value, "shape", None) if isinstance(shape, Sequence): diff --git a/packages/dimos-libero-pro-sidecar/uv.lock b/packages/dimos-libero-pro-sidecar/uv.lock new file mode 100644 index 0000000000..41159e3588 --- /dev/null +++ b/packages/dimos-libero-pro-sidecar/uv.lock @@ -0,0 +1,4889 @@ +version = 1 +revision = 3 +requires-python = ">=3.10, <3.13" +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11'", +] + +[[package]] +name = "absl-py" +version = "2.4.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" } +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" }, +] + +[[package]] +name = "addict" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/ef/fd7649da8af11d93979831e8f1f8097e85e82d5bfeabc8c68b39175d8e75/addict-2.4.0.tar.gz", hash = "sha256:b3b2210e0e067a281f5646c8c5db92e99b7231ea8b0eb5f74dbdf9e259d4e494", size = 9186, upload-time = "2020-11-21T16:21:31.416Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/00/b08f23b7d7e1e14ce01419a467b583edbb93c6cdb8654e54a9cc579cd61f/addict-2.4.0-py3-none-any.whl", hash = "sha256:249bb56bbfd3cdc2a004ea0ff4c2b6ddc84d53bc2194761636eb314d5cfa5dfc", size = 3832, upload-time = "2020-11-21T16:21:29.588Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "typing-extensions" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/67/58ded4b3f2e10f94972d8928050c85330e249a31dd45a0e5f3c0e9c3fa05/aiohttp-3.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e", size = 766140, upload-time = "2026-06-07T21:05:37.471Z" }, + { url = "https://files.pythonhosted.org/packages/18/68/4ae5b4e08943f316594bb68da89957d3baf5760588fa09509594bd777e4b/aiohttp-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491", size = 519430, upload-time = "2026-06-07T21:05:40.751Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c1/316c8f3549dbe5245f92bfd523ec6f32dd4d98cafe21df3f6a19b1184c75/aiohttp-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce", size = 514406, upload-time = "2026-06-07T21:05:42.111Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ee/fb0ac28684e8d753b83c8a4eebc19a5846912aa0a4daaabb6a9936363840/aiohttp-3.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3", size = 1703649, upload-time = "2026-06-07T21:05:43.427Z" }, + { url = "https://files.pythonhosted.org/packages/3b/57/aa2beab673331f111885db8a7b69dfe3ab0e53e446a0ace18ca694b4dc58/aiohttp-3.14.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505", size = 1675126, upload-time = "2026-06-07T21:05:44.897Z" }, + { url = "https://files.pythonhosted.org/packages/47/ea/dad128abe365e79be03b16ed464198ac73e0d257e8260c6f7d6f31cbef26/aiohttp-3.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521", size = 1771558, upload-time = "2026-06-07T21:05:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/63/f3/b5b4e10327cb85d34d24232c6b71b64602f190b3ccb238a043ac6b187dac/aiohttp-3.14.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd", size = 1856631, upload-time = "2026-06-07T21:05:47.844Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/93294c3045775c708ac8310eb3d3622a11d2951345ad590d532d62a1faa4/aiohttp-3.14.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb", size = 1714139, upload-time = "2026-06-07T21:05:49.982Z" }, + { url = "https://files.pythonhosted.org/packages/29/c4/93067c85a0373492ce8e577435203c5947c454af074ac48ed4f3a1b9dd4a/aiohttp-3.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42", size = 1588321, upload-time = "2026-06-07T21:05:51.431Z" }, + { url = "https://files.pythonhosted.org/packages/c4/39/9ff91aaf02af8b7b8222a987466da539f154c3e01732c22b5f5a20a8ee66/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b", size = 1670375, upload-time = "2026-06-07T21:05:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e4/77452a3676b8d99ac1375f77691d6bf65ea6e9f4b201b82ef77c916dc767/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192", size = 1690933, upload-time = "2026-06-07T21:05:54.902Z" }, + { url = "https://files.pythonhosted.org/packages/7d/84/b0059a7c7fc05ea23f3bc1596ba91c12f79588b9450564a24cac37536d0a/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05", size = 1740798, upload-time = "2026-06-07T21:05:56.458Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3a/e2a513ecbfc362591caa51a7f7e011b3bfc8938b388ae44cd95560d36999/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe", size = 1576412, upload-time = "2026-06-07T21:05:57.953Z" }, + { url = "https://files.pythonhosted.org/packages/a1/10/08f1654f538f93d36dcac66310a06eefce4641cdafca83f9f0a5317be254/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d", size = 1750199, upload-time = "2026-06-07T21:05:59.488Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/d91b70c57d8b8e9611e4a2e52238ca3698d3dc1c2efe25b7a9bf594ac584/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966", size = 1699356, upload-time = "2026-06-07T21:06:01.131Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f1/15340176f35ff61b95dbe34020bcf43f9e624a2d7bbac934715ff97d2033/aiohttp-3.14.1-cp310-cp310-win32.whl", hash = "sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6", size = 458939, upload-time = "2026-06-07T21:06:02.86Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/a2f1ec5b37f903109e43ae2862268cfe4a67a60c1b2cf43169fcdff5995f/aiohttp-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df", size = 482583, upload-time = "2026-06-07T21:06:04.666Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7a/7b56f6732ef79530afaa72aa335d41b67c8d79b946995f0b11ad72985435/aiohttp-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c", size = 453470, upload-time = "2026-06-07T21:06:06.322Z" }, + { url = "https://files.pythonhosted.org/packages/26/dd/bf526e6f0a1120dd6f2df2e97bacfe4d358f13d17a0ff5847301a1375a51/aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2", size = 765225, upload-time = "2026-06-07T21:06:07.957Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e1/a2872aa55495a70f61310d411541c6ee23812d9a884e000c716e1bc3edbf/aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f", size = 518743, upload-time = "2026-06-07T21:06:09.749Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e7/c60c7b209e509cc787de3cea0550a518538cfc08003e1c1e14c1c63fff71/aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8", size = 514139, upload-time = "2026-06-07T21:06:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8d/614ace2f579702c9840ab1e1447fd8509e35b0b904f7196418fa2f57b25d/aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04", size = 1784088, upload-time = "2026-06-07T21:06:12.887Z" }, + { url = "https://files.pythonhosted.org/packages/49/e0/726e90f99542bf292f81a96a12cc4847deb86f3ccf62c6f4014a201f4d33/aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8", size = 1737835, upload-time = "2026-06-07T21:06:14.564Z" }, + { url = "https://files.pythonhosted.org/packages/0b/4b/d176d5c4db9d33dacf0543102ea59503bc1d528af4cfd0b719949ca49389/aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6", size = 1842801, upload-time = "2026-06-07T21:06:16.228Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d6/5a99b563690ea0cbed912ae94a2ce33993a5709a651a3a4fe761e7dd973a/aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af", size = 1929992, upload-time = "2026-06-07T21:06:17.947Z" }, + { url = "https://files.pythonhosted.org/packages/76/7f/a987b14a3859094b3cea3f4825219c3e5536242564af6e3f9c2f6c994eb2/aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730", size = 1786989, upload-time = "2026-06-07T21:06:19.677Z" }, + { url = "https://files.pythonhosted.org/packages/f1/1a/420e5c85a3e73349372ed22ce0b6af86bfa6ce16a4b20a64a2e94608c781/aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621", size = 1640129, upload-time = "2026-06-07T21:06:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/a7/80/18a592ed3be0a402cc03670bd72ee1f8563ddbe1d8d5542dbf868f274136/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee", size = 1756576, upload-time = "2026-06-07T21:06:24.8Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0b/8b3d5713373858ff71a617daf6e3b0e81ad63e79d09a3cf2f6b6b983939c/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573", size = 1754668, upload-time = "2026-06-07T21:06:26.528Z" }, + { url = "https://files.pythonhosted.org/packages/9f/49/fd564575cf225821d7ba5a117cb8bc27213d8a7e1811162afb43ae077039/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7", size = 1817019, upload-time = "2026-06-07T21:06:28.297Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/e850c9ae6fc91356552ae668bb6c51e93fa29c8aef13398a10b56678557f/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf", size = 1631638, upload-time = "2026-06-07T21:06:30.242Z" }, + { url = "https://files.pythonhosted.org/packages/eb/94/3c337ba72451a89806ace6f75bddc92bafc5b8d53d90115a512858024b63/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85", size = 1835660, upload-time = "2026-06-07T21:06:31.943Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9c/9c18cf367a0498212d9ba7daf990b504a5e8ae064cda4b504e2647c89c03/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3", size = 1775698, upload-time = "2026-06-07T21:06:33.72Z" }, + { url = "https://files.pythonhosted.org/packages/b5/63/a251a9d2a6cb45065b2ddc0bde2b3dd10108740a9a42f632c66405a761a2/aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126", size = 458386, upload-time = "2026-06-07T21:06:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/17/ca/69274c51dcd6e8947d77b2806cf47a4a15f2c846e2cbeb1882547d3da283/aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5", size = 483406, upload-time = "2026-06-07T21:06:36.824Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8a/c25904f77690c3688ec140f87591ef11a0cfe36bf3d5c0f1f38056fb62b3/aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b", size = 452987, upload-time = "2026-06-07T21:06:38.371Z" }, + { url = "https://files.pythonhosted.org/packages/1d/21/151624b51cd92553d95424daf4bf19f19ce9be9002d19253e7e7ce67197b/aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480", size = 757402, upload-time = "2026-06-07T21:06:40.311Z" }, + { url = "https://files.pythonhosted.org/packages/c2/82/280619e0bd7bf2454987e19282616e84762255dd9c8468f62382e8c191f1/aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d", size = 512310, upload-time = "2026-06-07T21:06:42.207Z" }, + { url = "https://files.pythonhosted.org/packages/55/b2/2aac325583aaa1353045f96dffa586d8a34e8322e14a7ba49cffeb103ab4/aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2", size = 512448, upload-time = "2026-06-07T21:06:43.813Z" }, + { url = "https://files.pythonhosted.org/packages/8a/72/a60607cb849faa8af8a356c9329ea2eb6f395d49e82cc82ccba1fd8deb8f/aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2", size = 1766854, upload-time = "2026-06-07T21:06:45.391Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d3/d9fe1c9ec7557ab4d0d82bebaa728c6418f0b93295ec2f4ab015f7710cc7/aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a", size = 1740884, upload-time = "2026-06-07T21:06:47.413Z" }, + { url = "https://files.pythonhosted.org/packages/c1/dc/f2cecfaf9337ba3e63f181500814ff502aa3d00d9c7ec93a9d23d10a27b2/aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264", size = 1810034, upload-time = "2026-06-07T21:06:50.165Z" }, + { url = "https://files.pythonhosted.org/packages/66/d7/2ff65c5e65c0d7476daf7e15c032e0805e36811185b9623e3238ad6c763e/aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842", size = 1904054, upload-time = "2026-06-07T21:06:52.035Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/d445818389df371f56d141d881153ba23183c4735a03f7356ffb43f7757d/aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c", size = 1790278, upload-time = "2026-06-07T21:06:54.049Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/bf04cb4d865fc6101c2229a294ad744973b72e513fdc5a6b791e6983d72a/aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95", size = 1591795, upload-time = "2026-06-07T21:06:55.911Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b4/4dac0038960427ba832f6609dfb4ea5437d7fd80c72001b9e48f834f428b/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199", size = 1728397, upload-time = "2026-06-07T21:06:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/7cd4e8ad7aa3b75f17d56bb5498dd604a93d4e6eece822ba0568c413fff0/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817", size = 1766504, upload-time = "2026-06-07T21:07:00.009Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/fc01d9fcad0f73fed3f3d361f1f94f975947b50dff82919f6dc2bf4316cc/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a", size = 1777806, upload-time = "2026-06-07T21:07:02.064Z" }, + { url = "https://files.pythonhosted.org/packages/41/09/47e2d090bddcc8fb4ccb4c314aadc32d7c5d9bb55f50f6ad1c92fc15d501/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4", size = 1580707, upload-time = "2026-06-07T21:07:03.942Z" }, + { url = "https://files.pythonhosted.org/packages/3d/36/f1a4ce904ae0b6930cfe9afc96d0896f7ec1a620c400405d63783bb95a9c/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087", size = 1798121, upload-time = "2026-06-07T21:07:05.987Z" }, + { url = "https://files.pythonhosted.org/packages/70/0a/e0075ce9ca0279ee1d4f0c0b85f54fea02ebc83c3007651a72bece658fec/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3", size = 1767580, upload-time = "2026-06-07T21:07:07.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/a0c0a8f327a9c52095cdd8e312391b00d3ed64ab6c72bb5c33d8ec251cf7/aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4", size = 452771, upload-time = "2026-06-07T21:07:09.669Z" }, + { url = "https://files.pythonhosted.org/packages/df/d9/ea367c75f16ac9c6cdc8febb25e8318fa21a2b1bc8d6514d4b2d890bface/aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271", size = 479873, upload-time = "2026-06-07T21:07:11.538Z" }, + { url = "https://files.pythonhosted.org/packages/03/64/8d96784a7851156db8a4c6c3f6f91042fdf39fb15a4cc38c8b3c14833c45/aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847", size = 448073, upload-time = "2026-06-07T21:07:13.637Z" }, +] + +[[package]] +name = "aiohttp-jinja2" +version = "1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "jinja2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/39/da5a94dd89b1af7241fb7fc99ae4e73505b5f898b540b6aba6dc7afe600e/aiohttp-jinja2-1.6.tar.gz", hash = "sha256:a3a7ff5264e5bca52e8ae547bbfd0761b72495230d438d05b6c0915be619b0e2", size = 53057, upload-time = "2023-11-18T15:30:52.559Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/90/65238d4246307195411b87a07d03539049819b022c01bcc773826f600138/aiohttp_jinja2-1.6-py3-none-any.whl", hash = "sha256:0df405ee6ad1b58e5a068a105407dc7dcc1704544c559f1938babde954f945c7", size = 11736, upload-time = "2023-11-18T15:30:50.743Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "antlr4-python3-runtime" +version = "4.9.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/38/7859ff46355f76f8d19459005ca000b6e7012f2f1ca597746cbcd1fbfe5e/antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b", size = 117034, upload-time = "2021-11-06T17:52:23.524Z" } + +[[package]] +name = "anyio" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "bddl" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupytext" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/37/0211f82891a9f14efcfd2b2096f8d9e4351398ad637fdd1ee59cfc580b0e/bddl-1.0.1.tar.gz", hash = "sha256:1fa4e6e5050b93888ff6fd8455c39bfb29d3864ce06b4c37c0f781f513a2ae26", size = 164809, upload-time = "2022-03-08T01:48:23.564Z" } + +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + +[[package]] +name = "bleak" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "dbus-fast", marker = "sys_platform == 'linux'" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-corebluetooth", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-libdispatch", marker = "sys_platform == 'darwin'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-bluetooth", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-bluetooth-advertisement", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-bluetooth-genericattributeprofile", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-enumeration", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-devices-radios", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-foundation", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-foundation-collections", marker = "sys_platform == 'win32'" }, + { name = "winrt-windows-storage-streams", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/df/05a3f80ca8e3f7f5b0dba68a9e618147c909ccdba1468f07487dc8d72a9d/bleak-3.0.2.tar.gz", hash = "sha256:c2229cb8238d5876b4bd05c74bf7a1aea1f88da39d2e51ac9dfd5cc319d5265f", size = 125293, upload-time = "2026-05-02T23:01:04.066Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/54/05aceb9cd80073805b3ed8522e3196e8cb22f70e741873fa51406c31f4e7/bleak-3.0.2-py3-none-any.whl", hash = "sha256:39092feb9e83f1df5ad2f88e837723c7211c982ce9e9cda6235104bc2ebe0d0d", size = 146490, upload-time = "2026-05-02T23:01:02.592Z" }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + +[[package]] +name = "certifi" +version = "2026.6.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +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/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, +] + +[[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/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, + { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, + { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, + { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, + { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + +[[package]] +name = "cmeel" +version = "0.60.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/46/ddc7df697e49cae32b1a97b3e1a3b47b815238f9059312f987bc62a2e756/cmeel-0.60.1.tar.gz", hash = "sha256:3e0b92eb933a3693ad3f1da8aae31defcbee5f25969daaf20e59c57d6a9474cf", size = 14972, upload-time = "2026-05-11T17:13:57.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/39/f2db2ff475d42222c70fe25c737028aeaafdd9c0aeba04e6b2dc66e7f93f/cmeel-0.60.1-py3-none-any.whl", hash = "sha256:3f92b68353a58b4d6b5a664ea96bf58a3fef0891a8ed570d3c153361bbbb94b7", size = 20612, upload-time = "2026-05-11T17:13:55.812Z" }, +] + +[[package]] +name = "cmeel-assimp" +version = "6.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "cmeel-zlib" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/57/444bedd35567a8d6b61850b03aadf6b0d2d20737ee79c620c0e40f9a56dc/cmeel_assimp-6.0.5-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:840ab4bd398abbec5a612a731bb6f7fdc4e21b2708ae0dc0f99d7ff6d9992624", size = 10057855, upload-time = "2026-05-20T16:36:19.775Z" }, + { url = "https://files.pythonhosted.org/packages/6f/96/d4d128e074f59b79e90c78b8379127bd8953c72d1ad8a65229c23a09814f/cmeel_assimp-6.0.5-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d81f913e9efc8526a335900325906b85dc57891eeb7ece463714a107f14f63f", size = 9216472, upload-time = "2026-05-20T16:36:22.369Z" }, + { url = "https://files.pythonhosted.org/packages/77/37/11de8f263a5cde3cde4796e344ff034674bdbb7a2381f6b5303d49b24def/cmeel_assimp-6.0.5-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:db6fe58f8bd633cb05ab9f390c576a4e774927ed8c343083fad4f939e834bf6e", size = 13709230, upload-time = "2026-05-20T16:36:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/62/66/7f12b2f003671828a46ab35964aed0a34632fcba5638b4049bdd918cf342/cmeel_assimp-6.0.5-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:7b5ee36f3027de9b1bd73bd4294b60fed1bbc6c1524f94f64f69f38c5a49bf03", size = 14840275, upload-time = "2026-05-20T16:36:28.311Z" }, +] + +[[package]] +name = "cmeel-boost" +version = "1.90.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/01/ab2ea5b1ceae6283eb68be8beac529f294c70ac49d40b7873b0b639a7784/cmeel_boost-1.90.0.tar.gz", hash = "sha256:747d6d932838df26f50cdcb5983fa7532cad1d9ccce46c9f9d8b27b20a115981", size = 4085, upload-time = "2026-05-20T15:19:13.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/30/e0f27bd54396fe4d3eb1657aadde8e49f012f0dae667014e5c2682985561/cmeel_boost-1.90.0-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7f3e5b0ddb503337e7dc7f8a1200d3687bb24eb568c0cda71ddee12c8b77fbe", size = 30799110, upload-time = "2026-05-20T15:17:32.803Z" }, + { url = "https://files.pythonhosted.org/packages/36/09/5a89a5209c08c9e8d5a6d4a3bad9a7ae99f803bf714f89dea0b69f477359/cmeel_boost-1.90.0-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3666ee08702d8192657ef1badc80e08cadf69eaa27a8798895b9cc6249ee5144", size = 30693284, upload-time = "2026-05-20T15:17:37.227Z" }, + { url = "https://files.pythonhosted.org/packages/5a/6f/ab76aaf09bb9258ee115b9c8c939adf57fcf571cba4e59d2a83cbd170935/cmeel_boost-1.90.0-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6fc2ef59aecb088fd0e86c74dbdf7ec8c2e4f56ded0c95af87d967489b6ca479", size = 35829730, upload-time = "2026-05-20T15:17:43.073Z" }, + { url = "https://files.pythonhosted.org/packages/1f/cc/0498404b945ebfb658b8bfbb1efaee630519efcde6f6959ac91daa93e116/cmeel_boost-1.90.0-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d8ca4a91a78976ac243e33b51027efe71b35739e5f611dbdd992c388d3538acf", size = 36163159, upload-time = "2026-05-20T15:17:48.309Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/3e8c134393001b5f7dcf8c8aa1673b2126b271e1d475541a1fe0c1ca8bac/cmeel_boost-1.90.0-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0881a638b53d19340bab8bcc21b108999b20a8ed3a4db0a761b53b8116ad551", size = 30799100, upload-time = "2026-05-20T15:17:52.887Z" }, + { url = "https://files.pythonhosted.org/packages/9e/bf/3383e9bdf24176f9815c98f39a34901a9019a05b587d32d7e901e4fd5a91/cmeel_boost-1.90.0-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:853b2cfb491733d782ae163adc6378baaccd8dc9752870677498ccb0eaea125a", size = 30693292, upload-time = "2026-05-20T15:17:57.243Z" }, + { url = "https://files.pythonhosted.org/packages/d7/30/c19fdd54edbb1bef7d8c403752d7055ddbffcd757a947074517f3d18e414/cmeel_boost-1.90.0-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:653285a31001ad30a9b6fc95c0f86ee9f65b4ab5d868f649a2df3472db53d368", size = 35828987, upload-time = "2026-05-20T15:18:01.813Z" }, + { url = "https://files.pythonhosted.org/packages/07/52/ff72d2e3950a09e9fe9714d5952597f695efd5efcdc388d3781cec1910f0/cmeel_boost-1.90.0-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14efe34db660c9aacb61247a7f9ae0ee6d7626ec9a3d89b8b27e62dba6fcaa4d", size = 36162775, upload-time = "2026-05-20T15:18:06.66Z" }, + { url = "https://files.pythonhosted.org/packages/09/5c/a01c51d296f59bf36851e8d23387d7d09952c99ed9dbc26ed0c502ed05c3/cmeel_boost-1.90.0-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:506c315ff4b48c2e7d146787541319f22d9b63a96defba7f963e3402c5e5eca1", size = 30801862, upload-time = "2026-05-20T15:18:11.088Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3e/c6d597586bbef678f19c19f10b5b1f75203861d3acba41b2ec0cd3fca6d5/cmeel_boost-1.90.0-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34657195b40faf0d090a55ee736db4b69938241730b8786e4b3430e4487519de", size = 30695049, upload-time = "2026-05-20T15:18:15.845Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/3f590f6446afa13af6232c18020ed61c72418f94f060ae6cc30af28b6b7d/cmeel_boost-1.90.0-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0b9546ad7cdffd1d633bd878e8334adedb2efe099e72f965157e49c474cb463f", size = 35830172, upload-time = "2026-05-20T15:18:21.109Z" }, + { url = "https://files.pythonhosted.org/packages/32/67/c4dd452193fc017e80a8b0abd03c4bcbf45cebb21b87006268062cf2df8b/cmeel_boost-1.90.0-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c306b9af69d13502a6598672a341deb4f549fa3eb8cfb363de190e7409919a4a", size = 36167681, upload-time = "2026-05-20T15:18:26.397Z" }, +] + +[[package]] +name = "cmeel-console-bridge" +version = "1.0.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/13/2e9e9d23db8548aef975564055bdb4fb6da8a397a1e7df8cb61f5afebefb/cmeel_console_bridge-1.0.2.3.tar.gz", hash = "sha256:3b2837da7ab408e9d1a775c83c0a7772356062b3a3672e4ce247f2da71a8ecd9", size = 262061, upload-time = "2025-03-19T18:22:06.845Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/a7/527fa060e5881acb3b0a07bf1d803ccb831cb87739abb62b6bcd14f5aed3/cmeel_console_bridge-1.0.2.3-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:7aa19b2d006073a1fad55d32968c7d0c7136749e06f98405f4f73a71038a5c41", size = 21341, upload-time = "2025-03-19T18:21:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/bb/db/f8643a8766e8909e0dbfcda6191ca92454cf9a3fadd89be417db261601a1/cmeel_console_bridge-1.0.2.3-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c47d8c97cb120feed1c01f30845d16c67e4e8205941e3977951018972b9b8721", size = 21286, upload-time = "2025-03-19T18:21:57.984Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b4/9c79177152a220ab2e4ffa0140722165035f6a5c2abbed2912352bd7e7b9/cmeel_console_bridge-1.0.2.3-0-py3-none-manylinux_2_17_i686.whl", hash = "sha256:cad9723ac44ab563cd23bf361b604733623d11847c4edf2a2b4ebd1d984ade09", size = 23740, upload-time = "2025-03-19T18:21:59.683Z" }, + { url = "https://files.pythonhosted.org/packages/92/65/5741de6f550fe701d0780546d97b283306676315a3e1f379a6038e8c0ab0/cmeel_console_bridge-1.0.2.3-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:372942e9c44f681bfff377fba25b348801283aa6f3826a00e4195089bda9737a", size = 25762, upload-time = "2025-03-19T18:22:01.055Z" }, + { url = "https://files.pythonhosted.org/packages/50/a5/70e23c5570506bb39b56aa4d0f3a4a414e38082ddb33e86a48b546620121/cmeel_console_bridge-1.0.2.3-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:5bb1115ed38441b2396e732e10ec63d1e68445674f9f5d321f7985eb10e9aeef", size = 24477, upload-time = "2025-03-19T18:22:02.091Z" }, + { url = "https://files.pythonhosted.org/packages/47/36/bfd5a255348902e39243ccc6eba693bce714b891cd3be5603a9bd50c6de5/cmeel_console_bridge-1.0.2.3-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b8d084b797f592942208c2040b08e06b82f8832aa6c5e582ba6f1a4a653505b", size = 24970, upload-time = "2025-03-19T18:22:03.075Z" }, + { url = "https://files.pythonhosted.org/packages/b3/02/3ae074e9acb9e150a4d5d97f341c2064573cd5fe9e5af20ab58bf8c0020a/cmeel_console_bridge-1.0.2.3-0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:fb6753a9864217d969c4965389d66a476ac978136c03eadf1063b1619c359220", size = 24689, upload-time = "2025-03-19T18:22:04.084Z" }, + { url = "https://files.pythonhosted.org/packages/69/d0/321f74b7d4167a6c59bb7714a6899ba402d9fad611f62573b9d646107320/cmeel_console_bridge-1.0.2.3-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9d446c0fc541413d8d2ceea3c1cfb9cbfd57938d6659c113121eca6c245caafe", size = 24404, upload-time = "2025-03-19T18:22:05.232Z" }, +] + +[[package]] +name = "cmeel-octomap" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/ab/2fed2dbee13e4b39949591685419f1dbb691295e32a6bbbaf87edc005922/cmeel_octomap-1.10.0.tar.gz", hash = "sha256:bd79d1d17adede534de242e42e13ef0d9f04bdd27daf7d56c57f7c43670c9b05", size = 1694189, upload-time = "2025-01-06T17:57:05.477Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/22/ea67d35df31ec4bb2ed6e594b173c572c72dbd2a87e96906eac67b4af930/cmeel_octomap-1.10.0-4-py3-none-macosx_14_0_arm64.whl", hash = "sha256:c116eb151920d26ee2b2c1f656cd7526862006739817205f11f9366ab0ef6cb4", size = 639956, upload-time = "2025-01-06T17:56:56.826Z" }, + { url = "https://files.pythonhosted.org/packages/b2/da/07725a8c11224881f536ad252e97a3d9801b48e5e776017d5f00fb39b17f/cmeel_octomap-1.10.0-4-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:76cc42553f54bae97584aaf0c7bc33753ff287e2738aa2ecac4820121101dd46", size = 1044402, upload-time = "2025-01-06T17:56:58.553Z" }, + { url = "https://files.pythonhosted.org/packages/a2/15/9617b7039afd6d17d3148f6f970d953f5e265d7736f8fdbca09c86e976a0/cmeel_octomap-1.10.0-4-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:5fdb04546fff3accac5f8626c3fc15c3b99e94ab887793565e0b92cedaf96468", size = 1105037, upload-time = "2025-01-06T17:57:01.287Z" }, + { url = "https://files.pythonhosted.org/packages/51/69/88c1d1eca1abf2387ee8263ac7e12708c8b1b5b70b46a0bd9f43b485165b/cmeel_octomap-1.10.0-4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:84a7376cfced954bb7e3e347afbd02bdc1c83066b995afbdd0fb1e2d9f57ebec", size = 1108359, upload-time = "2025-01-06T17:57:04.085Z" }, + { url = "https://files.pythonhosted.org/packages/f5/59/57b3b38cf7a382855902b9d24266c283c29d977706438e6b7af62df74e2b/cmeel_octomap-1.10.0-5-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:042b4a21b5e5e19ee78a9a7db78e1b06fb8a287c832031788aec0d3fcabbfecd", size = 748832, upload-time = "2025-02-12T11:57:34.252Z" }, + { url = "https://files.pythonhosted.org/packages/55/8b/f5ec7676808a48c0185e216c0da700e34cb13ba233f13a4557a5ec56324a/cmeel_octomap-1.10.0-5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:79c15a0ece5ca3746170088ef2a377dfb3df8326fafde9bdba688852219758b9", size = 706924, upload-time = "2025-02-12T11:57:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/09/50/56de5a4d9f8ca58100146f16f42c4e2fbb49c0957bfe40d3fd2bc910afe4/cmeel_octomap-1.10.0-5-py3-none-manylinux_2_17_i686.whl", hash = "sha256:e2923bf593ebdafed86b6f3890a122c62fbd9cc9f325d60dbecb72b6b60d78fb", size = 1073973, upload-time = "2025-02-12T11:57:37.914Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f5/8dddf5cdd31176288acd85cc8bf0262b7c3de81d5cb2cb33aa6646f44eb1/cmeel_octomap-1.10.0-5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:d9e6f9c826905e8de632e9df8cc20e59ce2eb5d1e0b368d8d4abbbc5c0829c1a", size = 1044533, upload-time = "2025-02-12T11:57:39.672Z" }, + { url = "https://files.pythonhosted.org/packages/d6/14/b85bd33bb05c9bb7e87b9ac8401793c12a80a6d594b3ca4bcb5e971a24b7/cmeel_octomap-1.10.0-5-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:b0b54fac180dce4f483afe7029c29cc55f6f2b21be8413e8e2275845b0c204d7", size = 1105199, upload-time = "2025-02-12T11:57:41.286Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/fe3360441159974ebdbb4c013a92ad0425d5f8bf414868d5161060e40660/cmeel_octomap-1.10.0-5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c8691e665bab7c12b6f51e6c5fbbb83ee6f91dce9d15d9d0387553950e7fb5ee", size = 1092962, upload-time = "2025-02-12T11:57:43.297Z" }, + { url = "https://files.pythonhosted.org/packages/82/a6/074166544cc0ce3a5d7844f97dfd13d1b3ec7bff6a6e2cfb18d66a671a7f/cmeel_octomap-1.10.0-5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:735c0ad84dacbbcc8c4237f127c57244c236b7d6c7500b5c45a4c225e19daac1", size = 1083321, upload-time = "2025-02-12T11:57:46.121Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a3/b19ea0d30837369091141b248936b0757ee17f58b809007399bad0b398e4/cmeel_octomap-1.10.0-5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f86a83f6bd60de290cd327f0374d525328369e76591e3ab2ad1bc0b183678c4", size = 1109207, upload-time = "2025-02-12T11:57:48.709Z" }, +] + +[[package]] +name = "cmeel-qhull" +version = "8.0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/dd/8d0bcfb18771b2ea02bf85dfbbc587c97b274496fb5419b72134eb69430b/cmeel_qhull-8.0.2.1.tar.gz", hash = "sha256:68e8d41d95f61830f2d460af1e4d760f0dbe4d46413d7c736f0ed701153ebe52", size = 1308055, upload-time = "2023-11-17T14:21:06.003Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/b4/d72ebd5e9ee711b68ad466e7bd4c0edcb45b0c2c8a358fdcdb64b092666a/cmeel_qhull-8.0.2.1-0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:39f5183a6e026754c3c043239bac005bf1825240d72e1d8fdf090a0f3ea27307", size = 2804225, upload-time = "2023-11-17T14:15:39.958Z" }, + { url = "https://files.pythonhosted.org/packages/29/dc/4bfb8d51a09401cf740e66d10bdb388eacd7c73bae12ef78149cbbc93e83/cmeel_qhull-8.0.2.1-0-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:f135c5a4f4c8ed53f061bc86b794aaca2c0c34761c9269c06b71329c9da56f82", size = 2972481, upload-time = "2023-11-17T14:20:58.418Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7c/74b5c781cbfc8e4a9bb73b71659cc595bc0163223fd700b18133dbcf2831/cmeel_qhull-8.0.2.1-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:17f519106df79aed9fc5ec92833d4958d132d23021f02a78a9564cdf83a36c7c", size = 3078962, upload-time = "2023-11-17T14:21:00.183Z" }, + { url = "https://files.pythonhosted.org/packages/b4/16/ef7b6201835ba2492753c9c91b266d047b6664507be42ec858e2b24673b5/cmeel_qhull-8.0.2.1-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:c513abafa40e2b8eb7cd3640e3f92d5391fbd6ec0f4182dbf9536934d8a8ea3e", size = 3194917, upload-time = "2023-11-17T14:21:01.879Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ae/200bdf257507e2c95d0656bf02278cd666d49f0a9e2e6d281ea76d7d085c/cmeel_qhull-8.0.2.1-0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:20a69cb34b6250aee1f018412989734c9ddcad6ff66717a7c5516fc19f55d5ff", size = 3290068, upload-time = "2023-11-17T14:21:03.828Z" }, + { url = "https://files.pythonhosted.org/packages/01/1b/de3fa6091ef58ab40f02653e777c8943acf7cec486184d6007885123571d/cmeel_qhull-8.0.2.1-1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:b5d47b113c1cb8f519bc813cf015d0d01f8ce5b08912733a24a6018f7caa6e96", size = 2902499, upload-time = "2025-02-12T11:51:16.999Z" }, + { url = "https://files.pythonhosted.org/packages/05/0c/5e5d9a033c683eb272508ccf560c03ac6bf5d397b038fe05f896a2283eaf/cmeel_qhull-8.0.2.1-1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:33a0169f4ee37d093c450195b0ef73d4fe0d9d62abb7899ebe79f778b36e1f36", size = 2773563, upload-time = "2025-02-12T11:51:19.893Z" }, + { url = "https://files.pythonhosted.org/packages/52/9b/00c73069348e60fbbdf6a5a10de046083f7d1ad36844958bbf12163ac688/cmeel_qhull-8.0.2.1-1-py3-none-manylinux_2_17_i686.whl", hash = "sha256:a577e76ac94d128f2966b137ead9f088749513df63749728e2b588f4564b7fdf", size = 3228684, upload-time = "2025-02-12T11:51:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/c0/4a/81b8c88b444935a64d8c83b41e662f696c36dd5937c3ca687113ac4778d0/cmeel_qhull-8.0.2.1-1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:fd0b2d4ce749b102c3cdead4588249befd34f1a660628f6bfc090ce942925aac", size = 3156051, upload-time = "2025-02-12T11:51:24.594Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c1/44874cd8bfc1e3f7cb15678c836c7a1d5537f34f5a727a0207e01f395598/cmeel_qhull-8.0.2.1-1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:2371a7c80a14f3e874876359ae3e3094861f081fcdd7a03987c3e880d14e07b9", size = 3262508, upload-time = "2025-02-12T11:51:27.147Z" }, + { url = "https://files.pythonhosted.org/packages/54/0e/425d9ce1f2a831025d39fa5b6479b856bd4d73614c9caa690ac72bbfca04/cmeel_qhull-8.0.2.1-1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:197c14c2006dbeba8f5a5771700a7afea72c1a441aab7cdeaaf10b4ed8c1137d", size = 3172646, upload-time = "2025-02-12T11:51:28.967Z" }, + { url = "https://files.pythonhosted.org/packages/00/c1/e973e287a7d793911b8e6497b17586e601a678f2379ba2c615f72bd76480/cmeel_qhull-8.0.2.1-1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:886d1be24b31842286ae42755af5c312a43a4199632826e4110185ec36dc5c6a", size = 3530837, upload-time = "2025-02-12T11:51:31.651Z" }, + { url = "https://files.pythonhosted.org/packages/fd/65/c6cd54f04b5fcaa4ec52f5b57692c1dcef812ff9ee86545e5607369d365e/cmeel_qhull-8.0.2.1-1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1a49ce7f8492c9a8b49f930e34cce75b5e9b9843b015033dd0a25421441159fc", size = 3301908, upload-time = "2025-02-12T11:51:34.53Z" }, +] + +[[package]] +name = "cmeel-tinyxml2" +version = "11.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/96/4311533fee0a364bb605b585762f04c249f47857b33548a8ea837a7eb860/cmeel_tinyxml2-11.0.0.tar.gz", hash = "sha256:85d9c7680b3369af4c6b40a0dce70bbd84aa67832755622e57eb260cd95abe40", size = 645900, upload-time = "2026-05-21T11:49:32.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/f0/90c1640c53b623359d75ab1c70bdf19dc0afe82722bc5df57d09f8eaf83a/cmeel_tinyxml2-11.0.0-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:b0bd974e549b8c444626671a8e645897603ebf5225734cbe04a9dd3461477754", size = 111719, upload-time = "2026-05-21T11:49:25.999Z" }, + { url = "https://files.pythonhosted.org/packages/56/40/166447150a31bc3b794ffb493d5a634f67ffbc75dd8b4c46373701b7ef15/cmeel_tinyxml2-11.0.0-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9a1406f408262c37ae7c4566b1d67801c4b10c4980903fb1ef0ba45fa4407072", size = 109146, upload-time = "2026-05-21T11:49:27.829Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ca/3cc665afe2d76999f15454bb3b2f7c05f0088ad7de35718648291a536fd9/cmeel_tinyxml2-11.0.0-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:6f830007917c3e36f26b27d170ce84a619a62f46104d3cce435dff0125dd665f", size = 157109, upload-time = "2026-05-21T11:49:29.358Z" }, + { url = "https://files.pythonhosted.org/packages/87/4e/dcc0d9756d93be734d824e2a570cc9ac68909a1d7d3b6fc87c2fb32726c0/cmeel_tinyxml2-11.0.0-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:18674156bd41f3993dc1d5199da04fa496674358daa6588090fb9f86c71917b0", size = 148825, upload-time = "2026-05-21T11:49:31.035Z" }, +] + +[[package]] +name = "cmeel-urdfdom" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "cmeel-console-bridge" }, + { name = "cmeel-tinyxml2" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/75/4e8aff079e98582aeeb8e752805081da0c2dea405e79bafeefb555defe9f/cmeel_urdfdom-6.0.0.tar.gz", hash = "sha256:65c0fdc6021300fc55b2d0c03ab64dedc328034a74e40498e671bc894bb1dcf7", size = 303688, upload-time = "2026-05-21T12:08:56.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/40/51ba667135f01631179eee1614557193f8453740f248302d1b8b7f9f693e/cmeel_urdfdom-6.0.0-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:53d55cebb137a6e4dac6c16fa53f2dc2b7b9b5cda644bd1637a5bb849cd96e52", size = 381501, upload-time = "2026-05-21T12:08:48.758Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d1/2b49a8c940fa75abc13df9842c14e577e6a82d5854b6d52597ce3bb04894/cmeel_urdfdom-6.0.0-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0ef424735bd30f4afa4d1b4ddca9b297498c43005ddd775c080e55f62e9e0466", size = 377159, upload-time = "2026-05-21T12:08:50.485Z" }, + { url = "https://files.pythonhosted.org/packages/db/ac/0efde3a48220b55707bafb6d2e2dcca562f99dcd5c2c15311f7696eeacce/cmeel_urdfdom-6.0.0-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:0436f5230f1484c8e583284ef48c7291b230ada3dc5fb2937941f582e72409ec", size = 506000, upload-time = "2026-05-21T12:08:52.273Z" }, + { url = "https://files.pythonhosted.org/packages/32/d4/dfd617e598100e4e53ae3d228a968facff80bae53038fb18e2dccb1ab03a/cmeel_urdfdom-6.0.0-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:7ab1be680a8ec866d5422c617b641d1f0e38774061df28b8b426fb26edce6337", size = 530049, upload-time = "2026-05-21T12:08:54.224Z" }, +] + +[[package]] +name = "cmeel-zlib" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/85/9c6b77d5c49b363b17ba974271d58730bd26cbe00b1c576596339bb624ea/cmeel_zlib-1.3.2.tar.gz", hash = "sha256:6e31f2956c334de9a7e19a4e4f8eed030ed8c8016c841ea57226ce8bed443712", size = 3200, upload-time = "2026-05-20T16:24:37.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/e7/c60533fb6f638fe92d56b79edbcff70ed74a7f19b3ace981532efab5c3dd/cmeel_zlib-1.3.2-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:88a89882340391aa263ed1f7448fe0177e6fb9b93740cad381228437879f16a9", size = 1035696, upload-time = "2026-05-20T16:24:28.538Z" }, + { url = "https://files.pythonhosted.org/packages/85/f6/dfcc8b9c2989e7342aaac1781d147b06b31197337d1968029216be26ee43/cmeel_zlib-1.3.2-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:94a2f1adcc811617eab849b5771e608fa20e2fc44fb8cbd2e56a3e4d496461c1", size = 957505, upload-time = "2026-05-20T16:24:30.151Z" }, + { url = "https://files.pythonhosted.org/packages/3c/e9/6a3d6732e23fcf7072973f9ca8251eaef3e6738e46d6846064dd92e13331/cmeel_zlib-1.3.2-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:3b07fb7e28aaa917e6accb59a29dd0f5c1d272d42988f32ba2c232f5f455be4c", size = 1055975, upload-time = "2026-05-20T16:24:31.561Z" }, + { url = "https://files.pythonhosted.org/packages/70/a4/d345605b6f8c9d665baa1dd2f839ad6aee0d771e174be12c81fdd53067ed/cmeel_zlib-1.3.2-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:4658000c5531273d14ca8f0250abcd2a2ad85b336f10a273a77ecb38611a5f5a", size = 1052274, upload-time = "2026-05-20T16:24:33.078Z" }, + { url = "https://files.pythonhosted.org/packages/9b/9d/83737c38cc106e1b4959393da7744d5180b9ab5eac0369fc6ba0ea6c6428/cmeel_zlib-1.3.2-0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:06cbf60a361ca18e8a4c46a238149cebcaf955047a60830705e130aa397b5116", size = 1057248, upload-time = "2026-05-20T16:24:34.822Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/b378182b0f33ed9e9c091b3d1e8d86a6f2d69add107087b2e09adcb12137/cmeel_zlib-1.3.2-0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2a3425a65a0adf0ddbb48d41204a0d9c13197bd693f7821d1450e8a969d77352", size = 1055409, upload-time = "2026-05-20T16:24:36.219Z" }, +] + +[[package]] +name = "coal" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "cmeel-assimp" }, + { name = "cmeel-boost" }, + { name = "cmeel-octomap" }, + { name = "cmeel-qhull" }, + { name = "eigenpy" }, + { name = "libcoal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/14/8cb072a3e5fb85cac4d8d1234351ca3eb4ecf100b87901ab912ec7e5135e/coal-3.0.3.tar.gz", hash = "sha256:18660bde4021af496343646fa0a0d1bcf0be392f432641e772dc423be5075f64", size = 1464421, upload-time = "2026-05-21T12:30:20.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/2a/aa46eb1568f2d6e8a121f0da15a7778e2156058cf141d646a69c46d35bc5/coal-3.0.3-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f28ec989f385b9ffa5a0e2457efbc54d37fc70a6fa9df8f8caeeb12e8a7f8296", size = 1611143, upload-time = "2026-05-21T12:29:45.56Z" }, + { url = "https://files.pythonhosted.org/packages/d2/a8/43c3d209ae76ae0ba78dc3398af1ad5f817832478e20652c258d5bfb2ce8/coal-3.0.3-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da9ae72679d17a675be2f204debe60823b6568c1817670788f55cf666d7389a3", size = 1505259, upload-time = "2026-05-21T12:29:47.449Z" }, + { url = "https://files.pythonhosted.org/packages/c4/55/a909441bec811dfbc1ab453cac9eb26d5fdba18349348625ea33ec49b48b/coal-3.0.3-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6641e1b9de4a10d7986749751c4507f9b1708e438621e361c512037e7d249ad2", size = 2218416, upload-time = "2026-05-21T12:29:48.889Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ca/3f92a4a4ccd1392d0cfc3f6019241ea4939ff8b5b59e2b2dd0e7f1819330/coal-3.0.3-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2f526bb6173f5a4aab5cb0485a99fed73e6179b11814272a41f9e787cd509ed6", size = 2216341, upload-time = "2026-05-21T12:29:50.499Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4d/6af55fff20a0705f54ea313c2fe64b090e3e60e50fdcb5bfdb430a22e391/coal-3.0.3-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e59370db33b4fb0e23da8f953b300b3eec591624803e3143d447be21f530b87", size = 1611149, upload-time = "2026-05-21T12:29:52.062Z" }, + { url = "https://files.pythonhosted.org/packages/9a/78/3323984a61b63515e1951e2f697d4250a55b54788bc65496e2824989d969/coal-3.0.3-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:22572a8f877488ab987f868ffd362c08f923470be6b1e4f9c86d260f110405fc", size = 1505271, upload-time = "2026-05-21T12:29:53.871Z" }, + { url = "https://files.pythonhosted.org/packages/1d/29/65550bafd2639b8152e6a0e63603e74f657761fc1dc5daa4a305b5ea9922/coal-3.0.3-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:b8aafdf317161ba73858ed1a3e8e673c5a82bd04cef82a3b7ba953d6a8d1007d", size = 2218087, upload-time = "2026-05-21T12:29:55.361Z" }, + { url = "https://files.pythonhosted.org/packages/43/45/e5859f9c03c6353e50ac8df218a6b9efb8de90f3fa1f3bd571bc6a3242cd/coal-3.0.3-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2c9a62cd52868ce295623e34e664084ee87b9bb6314df913952d288cbe8b3fe6", size = 2215823, upload-time = "2026-05-21T12:29:57.227Z" }, + { url = "https://files.pythonhosted.org/packages/08/82/1c634b366c7ba991f3f11b73bfff5c8cfee0ee32911347f37a256bc1724c/coal-3.0.3-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fd9c9bb7d4de17afda5e59802fddddee4149bdf72312cd60d68403b2d5265e9b", size = 1626797, upload-time = "2026-05-21T12:29:59.079Z" }, + { url = "https://files.pythonhosted.org/packages/33/5a/6c9836db4747010fc31543336a6b7f9aea04b1a58b8fe09611daf5e965f4/coal-3.0.3-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d3c157a72d3c79a99bd6b59479ab5ca0efeed22b74e5d8e66dda827ce76e18e", size = 1516483, upload-time = "2026-05-21T12:30:00.967Z" }, + { url = "https://files.pythonhosted.org/packages/56/46/8c7c7e83c83acdc5af8c553a2ca0b1b335132dccff4732edd6acd90a5a16/coal-3.0.3-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f9736086902db07b3b38b04917e7d3c9291d12340e5248c06b327ce6a6e2de49", size = 2190068, upload-time = "2026-05-21T12:30:02.692Z" }, + { url = "https://files.pythonhosted.org/packages/19/f1/25368a2fb63a18283b6732e2cdbe15bfb19df26b76f3721fd79729def54a/coal-3.0.3-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b98f45b92af8f6608a97d47f05abab861af13ba54a47c82dfc22c9f4e7754a18", size = 2201644, upload-time = "2026-05-21T12:30:04.333Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +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 = "configargparse" +version = "1.7.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/0b/30328302903c55218ffc5199646d0e9d28348ff26c02ba77b2ffc58d294a/configargparse-1.7.5.tar.gz", hash = "sha256:e3f9a7bb6be34d66b2e3c4a2f58e3045f8dfae47b0dc039f87bcfaa0f193fb0f", size = 53548, upload-time = "2026-03-11T02:19:38.144Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/19/3ba5e1b0bcc7b91aeab6c258afd70e4907d220fed3972febe38feb40db30/configargparse-1.7.5-py3-none-any.whl", hash = "sha256:1e63fdffedf94da9cd435fc13a1cd24777e76879dd2343912c1f871d4ac8c592", size = 27692, upload-time = "2026-03-11T02:19:36.442Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, +] + +[[package]] +name = "cryptography" +version = "49.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" }, + { url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" }, + { url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" }, + { url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" }, + { url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" }, + { url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" }, + { url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" }, + { url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" }, + { url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" }, + { url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" }, + { url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" }, + { url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" }, + { url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" }, + { url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" }, + { url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" }, + { url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/63/d3/4a83af35d65e3fad632c926fad684c193ea4398569ccb0bbbc7fe8f5dc9a/cryptography-49.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b", size = 3993685, upload-time = "2026-06-12T20:02:14.883Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a7/f9dac0ab7f80368c56993a7bf638ef9935f825c91902798481fac0898138/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838", size = 4676239, upload-time = "2026-06-12T20:02:28.793Z" }, + { url = "https://files.pythonhosted.org/packages/d7/70/2ba3769dd0ae167e2f33dfa9592d45db6ff9a61d62ca1a5b3d1bdd09068f/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5", size = 4715584, upload-time = "2026-06-12T20:01:27.495Z" }, + { url = "https://files.pythonhosted.org/packages/94/64/2923570ac1c0bd3a737aa366ac3abbbbde273042308b8cde95e2364a6e6a/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615", size = 4675885, upload-time = "2026-06-12T20:01:55.49Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f8/614dc7e051418cfe53d55173c1e24c6b0085e89996fe90508c2fdf769aef/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6", size = 4715449, upload-time = "2026-06-12T20:02:05.469Z" }, + { url = "https://files.pythonhosted.org/packages/aa/50/a9caea39ad19c431c1a3f8a31114df65b260cdfe67786b6c7e7c040c4c44/cryptography-49.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6", size = 3783731, upload-time = "2026-06-12T20:02:43.319Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "13.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/21/8464d133752951c154feafb3b65c297e7d80f301183d220bec4c830f1441/cuda_bindings-13.3.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:120fcc53d57903df529c3486962c56528cba5b7d6c57c99537320ed9922c8b86", size = 6073403, upload-time = "2026-05-29T23:11:36.22Z" }, + { url = "https://files.pythonhosted.org/packages/a8/1f/5ef51f5fbaa5d4d3201bb3d7555af028ec1aa4416275ccbf73c9e34e3d2d/cuda_bindings-13.3.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9851b0caa8bfd3bc6fa054eaf57bea7c8e9c3a62db2d2621224677f49f3c53d0", size = 6675244, upload-time = "2026-05-29T23:11:38.664Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/457ca12dad3ee9bfcc9a545cfd6b64b359ba49de40f776f6e028e678f262/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5879712accf6e14bb01aa5e67440eb84998b8d104b509cc7a6dc0b8f656a474", size = 6053539, upload-time = "2026-05-29T23:11:43.19Z" }, + { url = "https://files.pythonhosted.org/packages/95/7a/c5e3c34a409b148f5c0f5a4ea374158f95d488862c1dffedf9aa5c639df9/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04436a9364059c84b8f9636f359eccda1cf814341f5b670c71d80d2f79dbc708", size = 6674166, upload-time = "2026-05-29T23:11:45.478Z" }, + { url = "https://files.pythonhosted.org/packages/ce/67/5e7dba1ba576dd73da5dee894ca076ca5e959450dfff66d6d510a255d1f7/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7855c4868aabc0cfae28abbe83d56734bdfbd08f08fc234ac1912a12858bf49", size = 6025351, upload-time = "2026-05-29T23:11:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/6d2e9047d1fb243dbaa364b01e0297534b9ed7fd27dba1c9f361519cf69b/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e32d08f71ebcdf00f0f41eab2eb37e8da94c8ed411cc9f7f7a019ce6b34abe3a", size = 6657965, upload-time = "2026-05-29T23:11:52.227Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/53/8fc9b0cdc5b7f62746e6a01b85b6461e5ae27f871010a5fcf8fa6950766d/cuda_pathfinder-1.5.6-py3-none-any.whl", hash = "sha256:7e4c07c117b78ba1fb35dac4c444d21f3677b1b1ff56175c53a8e3025c5b43c0", size = 52972, upload-time = "2026-06-30T00:58:04.34Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +curand = [ + { name = "nvidia-curand", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "(python_full_version < '3.11' and sys_platform == 'win32') or sys_platform == 'linux'" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "dash" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "importlib-metadata" }, + { name = "janus" }, + { name = "mcp" }, + { name = "nest-asyncio" }, + { name = "plotly" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "retrying" }, + { name = "setuptools" }, + { name = "typing-extensions" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/3c/0608ea83920ddbd27de3aeb60d48e56efb3390c2cfb44dabfd8dc25c0ba2/dash-4.3.0.tar.gz", hash = "sha256:1ec96641d5e3c06b7dfffad8241e345533d70327d96f722a8e13b827c287341b", size = 8095642, upload-time = "2026-06-19T14:56:03.655Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/ad/0a230dc6cd85bb38e7fe6acc9fbaeb19ddf16ababcb54038ae81128da591/dash-4.3.0-py3-none-any.whl", hash = "sha256:cbbe79a2bb1a5e46fbbbddc33f9f558419442c2d55cd12526078f489b5801cc0", size = 8445443, upload-time = "2026-06-19T14:55:59.024Z" }, +] + +[[package]] +name = "dbus-fast" +version = "5.0.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/db/b621610e50b1bc46ff63534d75239553c1bf33256de6096b58214fd9808a/dbus_fast-5.0.22.tar.gz", hash = "sha256:34dc67d7d21a12399828dd13e63b352750580beea54ea7c729e708f2d2905fef", size = 83224, upload-time = "2026-06-05T18:47:59.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/69/9235af850e2ee0cec23b4e863612492964a9c40b65f3002e4a9f32e1ffa2/dbus_fast-5.0.22-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8244561e503cffcd5e6b1ff6544a44aa0b65b6856f2f64d04345ebf82f8ac48", size = 840236, upload-time = "2026-06-05T18:55:41.007Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d1/08ed17331dd23a8b91b80db9acca48301c4879352024dc7882e2c5893fa2/dbus_fast-5.0.22-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32869678240f233ece5a1137960c0337699dbb39acc02c155929f52543f03fb", size = 884309, upload-time = "2026-06-05T18:55:42.447Z" }, + { url = "https://files.pythonhosted.org/packages/34/ed/02458af54460e55cf26fdf0a778aa9b3217ac8d37e6280aa68e19f176650/dbus_fast-5.0.22-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f74700b1b4fd58acb76f8790e2cd475084d0e59149e0cc035abd239435a339b7", size = 885179, upload-time = "2026-06-05T18:55:43.801Z" }, + { url = "https://files.pythonhosted.org/packages/e9/79/4958c807e59106558a4415212e6be019775f5be9775ffc79c6bc7248b388/dbus_fast-5.0.22-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:246780dcda59d26dd2fb1d9a15991bc8483e9c241cee8713920a39a363ad4d39", size = 847693, upload-time = "2026-06-05T18:55:45.271Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/a5322fbc172ee099f98186224cf7e3bf48fc8bcb08270951da8006b556ba/dbus_fast-5.0.22-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:58e4d41f9fea22cfd6dacdf03d53b932bbe15a825af5abad3ca79b91493a15c3", size = 882773, upload-time = "2026-06-05T18:55:46.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e7/e068899c1d9a034ed9d5fc8519b8e2247aa8c28a6a67229162db64163b59/dbus_fast-5.0.22-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d8ca39f3f2ab34f88bb343db87c7cf8b07eefa046b23d8c38b3387f150ac609e", size = 891573, upload-time = "2026-06-05T18:55:48.34Z" }, + { url = "https://files.pythonhosted.org/packages/ec/84/dfb014de75a3a854dccaae1cce8f840e4312e3efc781768eedd60d25d9ef/dbus_fast-5.0.22-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:846f9a6602b4383f989201f7851459fb225a8912cd24b38e63894748545c3040", size = 838836, upload-time = "2026-06-05T18:55:51.51Z" }, + { url = "https://files.pythonhosted.org/packages/70/c2/be41bcc678e97092d44ba22d09ce687f76c955b3367a7e6863377b1cfea5/dbus_fast-5.0.22-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886b43446b6fdc3986befbbb88db1365b14e49dd0a7edf84c2c67ac66c7160a4", size = 883163, upload-time = "2026-06-05T18:55:53Z" }, + { url = "https://files.pythonhosted.org/packages/17/cf/336c08f88fdd813a39fc1603a10a15aec67115e08a895e7c9840df54d4d7/dbus_fast-5.0.22-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3a699fca957acc845ddb12b47f741dba23ce147fdb93583e0c7e7bad3e9b2355", size = 886852, upload-time = "2026-06-05T18:55:54.434Z" }, + { url = "https://files.pythonhosted.org/packages/04/f1/7c1aa53f25252a2317f21ad6e8eaa125246d2585ea4611f3f10a6feaeeb1/dbus_fast-5.0.22-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9a5b05fd4973862042e5bee2c5e8c5a15297e0b33a975bf25b44becf7bcb3618", size = 846497, upload-time = "2026-06-05T18:55:56.193Z" }, + { url = "https://files.pythonhosted.org/packages/4c/32/a981ef2305f1bf41e538e02fa0cd69614f9043fd00ca965bf3044416c79b/dbus_fast-5.0.22-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6c4dae5292a7924ec062815c34b49043d8386cd22e165f9fb4012de00997cdf1", size = 882275, upload-time = "2026-06-05T18:55:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/d9/a0/78800a172f4ca32e19a70a36e175f54831f33a497c9644f3a3fa4dce01ea/dbus_fast-5.0.22-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b7d90a52be79acbaef257f3a81d5b9b9dec40f1bad29429ac5c7802684fb9b84", size = 890566, upload-time = "2026-06-05T18:55:59.407Z" }, + { url = "https://files.pythonhosted.org/packages/24/06/233b0bc13919474f70320bf389cb81ce02811956d6cf86c45e84679b63c3/dbus_fast-5.0.22-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f0bcad7f71d2304a68a5b0bc0d24c3fcc14710a2ffcf5f2a27521e3aece71ca", size = 799464, upload-time = "2026-06-05T18:56:02.617Z" }, + { url = "https://files.pythonhosted.org/packages/68/e9/77bc23a6f5aebfb8f2c34489795e8517aed7eca31738438e1a4c4a4891d3/dbus_fast-5.0.22-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ffcf16034f71a801bd2108aeffb6337d104c9459e8b1a218d16a917c8a2d2e9", size = 852687, upload-time = "2026-06-05T18:56:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/56769d0936d1273d1801ef574ec426ccb3f61f4b0a7a0eeb9eb2b8ccafa5/dbus_fast-5.0.22-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98de6d2c200d8182e1fd0bdde3206fa556b8fa14ebb752a044cd8daa87b4658c", size = 833814, upload-time = "2026-06-05T18:56:05.87Z" }, + { url = "https://files.pythonhosted.org/packages/06/ca/964f0d39a3be03b12a98f39519d34ad95b74360c7e3adf4cd4907dc25fd6/dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b013437b66dc22b8d9aca5e0b0d46bf1980208a143409469fe482d9684a2a717", size = 806891, upload-time = "2026-06-05T18:56:07.623Z" }, + { url = "https://files.pythonhosted.org/packages/f4/25/57fe6ab509ad9da2e190498fa9c37f868e38ac521d940a9edb9ba6b6c657/dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:855f15b7f7805171da2b82de1c317d01cfbb9fb8ac61fcc1e8dec54d8c69fab7", size = 830867, upload-time = "2026-06-05T18:56:09.473Z" }, + { url = "https://files.pythonhosted.org/packages/be/2b/da036e9f4aeb776833139575fe0774544aa6cd13ba997eea7fbc4ab99852/dbus_fast-5.0.22-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7d1c42963235cfc015a2d2b8c5fe42b65387493b4ad4ce0ec122601c805e6742", size = 860651, upload-time = "2026-06-05T18:56:10.952Z" }, +] + +[[package]] +name = "dimos" +version = "0.0.13" +source = { editable = "../../" } +dependencies = [ + { name = "bleak" }, + { name = "cryptography" }, + { name = "dimos-lcm" }, + { name = "dimos-viewer" }, + { name = "llvmlite" }, + { name = "lz4" }, + { name = "numba" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "open3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "open3d-unofficial-arm", marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, + { name = "opencv-python" }, + { name = "pin" }, + { name = "plotext" }, + { name = "plum-dispatch" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "pyturbojpeg" }, + { name = "reactivex" }, + { name = "rerun-sdk" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "sortedcontainers" }, + { name = "sqlite-vec" }, + { name = "structlog" }, + { name = "terminaltexteffects" }, + { name = "textual" }, + { name = "textual-serve" }, + { name = "toolz" }, + { name = "typer" }, + { name = "typing-extensions" }, + { name = "websocket-client" }, +] + +[package.metadata] +requires-dist = [ + { name = "a750-control", marker = "platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'manipulation'" }, + { name = "bleak", specifier = ">=3.0.2" }, + { name = "can-motor-control", marker = "platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'manipulation'", specifier = ">=0.0.2" }, + { name = "catkin-pkg", marker = "extra == 'grasp'", specifier = ">=1.1.0" }, + { name = "chromadb", marker = "extra == 'perception'", specifier = ">=1.0.0" }, + { name = "cryptography", specifier = ">=46.0.5" }, + { name = "cupy-cuda12x", marker = "platform_machine == 'x86_64' and extra == 'cuda'", specifier = "==13.6.0" }, + { name = "cyclonedds", marker = "extra == 'dds'", specifier = ">=0.10.5" }, + { name = "cyclonedds", marker = "extra == 'unitree-dds'", specifier = ">=0.10.5" }, + { name = "dimos", extras = ["agents", "apriltag", "base", "cpu", "cuda", "drone", "grasp", "learning", "manipulation", "manipulation-toppra", "misc", "perception", "sim", "unitree", "visualization", "web"], marker = "extra == 'all'" }, + { name = "dimos", extras = ["agents", "web", "perception", "visualization"], marker = "extra == 'base'" }, + { name = "dimos", extras = ["base", "mapping"], marker = "extra == 'unitree'" }, + { name = "dimos", extras = ["unitree"], marker = "extra == 'unitree-dds'" }, + { name = "dimos-lcm", specifier = ">=0.1.3" }, + { name = "dimos-viewer", specifier = "==0.32.0a1" }, + { name = "dimos-viewer", marker = "extra == 'visualization'", specifier = "==0.32.0a1" }, + { name = "drake", marker = "platform_machine != 'aarch64' and sys_platform == 'darwin' and extra == 'manipulation'", specifier = "==1.45.0" }, + { name = "drake", marker = "platform_machine != 'aarch64' and sys_platform != 'darwin' and extra == 'manipulation'", specifier = ">=1.40.0" }, + { name = "edgetam-dimos", marker = "extra == 'misc'" }, + { name = "einops", marker = "extra == 'perception'", specifier = ">=0.8.1" }, + { name = "fastapi", marker = "extra == 'web'", specifier = ">=0.115.6" }, + { name = "faster-whisper", marker = "extra == 'agents'", specifier = ">=1.0.0" }, + { name = "ffmpeg-python", marker = "extra == 'web'" }, + { name = "gdown", marker = "extra == 'misc'", specifier = ">=5.2.2" }, + { name = "googlemaps", marker = "extra == 'misc'", specifier = ">=4.10.0" }, + { name = "gtsam-extended", marker = "extra == 'mapping'", specifier = ">=4.3a1.post1" }, + { name = "h5py", marker = "extra == 'learning'" }, + { name = "hydra-core", marker = "extra == 'perception'", specifier = ">=1.3.0" }, + { name = "ipykernel", marker = "extra == 'misc'" }, + { name = "jinja2", marker = "extra == 'web'", specifier = ">=3.1.6" }, + { name = "langchain", marker = "extra == 'agents'", specifier = ">=1.2.3,<2" }, + { name = "langchain-core", marker = "extra == 'agents'", specifier = ">=1.2.22,<2" }, + { name = "langchain-huggingface", marker = "extra == 'agents'", specifier = ">=1,<2" }, + { name = "langchain-ollama", marker = "extra == 'agents'", specifier = ">=1,<2" }, + { name = "langchain-openai", marker = "extra == 'agents'", specifier = ">=1,<2" }, + { name = "lap", marker = "extra == 'perception'", specifier = ">=0.5.12" }, + { name = "llvmlite", specifier = ">=0.42.0" }, + { name = "lz4", specifier = ">=4.4.5" }, + { name = "matplotlib", marker = "extra == 'grasp'", specifier = ">=3.7.1" }, + { name = "matplotlib", marker = "extra == 'manipulation'", specifier = ">=3.7.1" }, + { name = "mcap", marker = "extra == 'unitree-dds'", specifier = ">=1.2.0" }, + { name = "moondream", marker = "extra == 'perception'" }, + { name = "mujoco", marker = "extra == 'sim'", specifier = ">=3.3.4" }, + { name = "numba", specifier = ">=0.60.0" }, + { name = "numpy", specifier = ">=1.26.4" }, + { name = "ollama", marker = "extra == 'agents'", specifier = ">=0.6.0" }, + { name = "omegaconf", marker = "extra == 'perception'", specifier = ">=2.3.0" }, + { name = "onnxruntime", marker = "extra == 'cpu'" }, + { name = "onnxruntime-gpu", marker = "platform_machine == 'x86_64' and extra == 'cuda'", specifier = ">=1.17.1" }, + { name = "open-clip-torch", marker = "extra == 'misc'", specifier = "==3.2.0" }, + { name = "open3d", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'", specifier = ">=0.18.0" }, + { name = "open3d-unofficial-arm", marker = "platform_machine == 'aarch64' and sys_platform == 'linux'", specifier = ">=0.19.0.post9" }, + { name = "openai", marker = "extra == 'agents'" }, + { name = "opencv-contrib-python", marker = "extra == 'apriltag'", specifier = "==4.10.0.84" }, + { name = "opencv-python" }, + { name = "pandas", marker = "extra == 'learning'" }, + { name = "pillow", marker = "extra == 'perception'" }, + { name = "pin", specifier = ">=3.3.0" }, + { name = "pin-pink", marker = "extra == 'manipulation'", specifier = ">=4.2.0" }, + { name = "piper-sdk", marker = "extra == 'manipulation'" }, + { name = "playground", marker = "extra == 'sim'", specifier = ">=0.0.5" }, + { name = "plotext", specifier = "==5.3.2" }, + { name = "plum-dispatch", specifier = "==2.5.7" }, + { name = "portal", marker = "extra == 'misc'" }, + { name = "protobuf", specifier = ">=6.33.5,<7" }, + { name = "psutil", specifier = ">=7.0.0" }, + { name = "pyarrow", marker = "extra == 'learning'" }, + { name = "pycollada", marker = "extra == 'manipulation'" }, + { name = "pydantic" }, + { name = "pydantic-settings", specifier = ">=2.11.0,<3" }, + { name = "pygame", marker = "extra == 'sim'", specifier = ">=2.6.1" }, + { name = "pymavlink", marker = "extra == 'drone'" }, + { name = "pyrealsense2-extended", marker = "sys_platform != 'darwin' and extra == 'manipulation'" }, + { name = "python-dotenv" }, + { name = "python-multipart", marker = "extra == 'misc'", specifier = ">=0.0.27" }, + { name = "pytorch-ignite", marker = "extra == 'grasp'" }, + { name = "pyturbojpeg", specifier = "==1.8.2" }, + { name = "pyyaml", marker = "extra == 'manipulation'", specifier = ">=6.0" }, + { name = "qpsolvers", extras = ["proxqp"], marker = "extra == 'manipulation'", specifier = ">=4.12.0" }, + { name = "reactivex" }, + { name = "reportlab", marker = "extra == 'apriltag'", specifier = ">=4.5.0" }, + { name = "rerun-sdk", specifier = "==0.32.0a1" }, + { name = "rerun-sdk", marker = "extra == 'visualization'", specifier = "==0.32.0a1" }, + { name = "roboplan", marker = "extra == 'manipulation'", specifier = ">=0.0.100" }, + { name = "roboplan", marker = "extra == 'manipulation-toppra'", specifier = ">=0.0.100" }, + { name = "scipy", specifier = ">=1.15.1" }, + { name = "sortedcontainers", specifier = "==2.4.0" }, + { name = "sounddevice", marker = "extra == 'agents'" }, + { name = "soundfile", marker = "extra == 'web'" }, + { name = "sqlite-vec", specifier = ">=0.1.6" }, + { name = "sse-starlette", marker = "extra == 'web'", specifier = ">=2.2.1" }, + { name = "structlog", specifier = ">=25.5.0,<26" }, + { name = "tensorboard", marker = "extra == 'misc'", specifier = "==2.20.0" }, + { name = "terminaltexteffects", specifier = "==0.12.2" }, + { name = "textual", specifier = "==3.7.1" }, + { name = "textual-serve", specifier = ">=1.1.1,<2" }, + { name = "timm", marker = "extra == 'misc'", specifier = ">=1.0.15" }, + { name = "toolz", specifier = ">=1.1.0" }, + { name = "torch", marker = "extra == 'grasp'" }, + { name = "torchreid", marker = "extra == 'misc'", specifier = "==0.2.5" }, + { name = "transformers", extras = ["torch"], marker = "extra == 'perception'", specifier = ">=4.53.0,<4.54" }, + { name = "trimesh", marker = "extra == 'manipulation'" }, + { name = "typer", specifier = ">=0.19.2,<1" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'", specifier = ">=4.7" }, + { name = "ultralytics", marker = "extra == 'perception'", specifier = ">=8.3.70" }, + { name = "unitree-sdk2py-dimos", marker = "extra == 'unitree-dds'", specifier = ">=1.0.2" }, + { name = "unitree-webrtc-connect", marker = "extra == 'unitree'", specifier = ">=2.1.2" }, + { name = "uvicorn", marker = "extra == 'web'", specifier = ">=0.34.0" }, + { name = "vgn", marker = "extra == 'grasp'", git = "https://github.com/ethz-asl/vgn.git?rev=d7af0622433f52ae88ebe81533f12b46b33e951a" }, + { name = "viser", extras = ["urdf"], marker = "extra == 'manipulation'", specifier = ">=1.0.29" }, + { name = "websocket-client", specifier = ">=1.8" }, + { name = "xacro", marker = "extra == 'manipulation'" }, + { name = "xarm-python-sdk", marker = "extra == 'manipulation'", specifier = ">=1.17.0" }, + { name = "xarm-python-sdk", marker = "extra == 'misc'", specifier = ">=1.17.0" }, +] +provides-extras = ["misc", "visualization", "learning", "agents", "web", "perception", "unitree", "unitree-dds", "manipulation", "manipulation-toppra", "grasp", "cpu", "cuda", "sim", "mapping", "drone", "dds", "base", "apriltag", "all"] + +[package.metadata.requires-dev] +autofix = [{ name = "ruff", specifier = "==0.14.3" }] +lint = [ + { name = "aiortc", specifier = ">=1.14.0" }, + { name = "chromadb", specifier = ">=1.0.0" }, + { name = "dimos", extras = ["web", "visualization"] }, + { name = "einops", specifier = ">=0.8.1" }, + { name = "gdown", specifier = "==6.0.0" }, + { name = "googlemaps", specifier = ">=4.10.0" }, + { name = "hydra-core", specifier = ">=1.3.0" }, + { name = "ipython" }, + { name = "langchain", specifier = "==1.2.3" }, + { name = "langchain-core", specifier = "==1.3.3" }, + { name = "langchain-openai", specifier = ">=1,<2" }, + { name = "lap", specifier = ">=0.5.12" }, + { name = "moondream" }, + { name = "mypy", specifier = "==1.19.0" }, + { name = "ollama", specifier = ">=0.6.0" }, + { name = "open-clip-torch", specifier = "==3.2.0" }, + { name = "openai" }, + { name = "openai-whisper" }, + { name = "pandas-stubs", specifier = ">=2.3.2.250926,<3" }, + { name = "pytest", specifier = "==8.3.5" }, + { name = "python-can", specifier = ">=4" }, + { name = "python-socketio", specifier = ">=5.16.1" }, + { name = "ruff", specifier = "==0.14.3" }, + { name = "sounddevice", specifier = ">=0.5.5" }, + { name = "tensorboard", specifier = "==2.20.0" }, + { name = "torch" }, + { name = "torchreid", specifier = "==0.2.5" }, + { name = "transformers", extras = ["torch"], specifier = "==4.53.3" }, + { name = "trimesh", specifier = ">=4.12" }, + { name = "types-pyaudio" }, + { name = "types-pyyaml", specifier = ">=6.0.12.20250915,<7" }, + { name = "types-reportlab", specifier = ">=4.5.0" }, + { name = "types-requests", specifier = ">=2.32.4.20260107,<3" }, + { name = "ultralytics", specifier = ">=8.3.70" }, + { name = "watchdog", specifier = ">=3.0.0" }, + { name = "xacro" }, +] +project-deps = [ + { name = "chromadb", specifier = ">=1.0.0" }, + { name = "dimos", extras = ["web", "visualization"] }, + { name = "einops", specifier = ">=0.8.1" }, + { name = "gdown", specifier = "==6.0.0" }, + { name = "googlemaps", specifier = ">=4.10.0" }, + { name = "hydra-core", specifier = ">=1.3.0" }, + { name = "langchain", specifier = "==1.2.3" }, + { name = "langchain-core", specifier = "==1.3.3" }, + { name = "langchain-openai", specifier = ">=1,<2" }, + { name = "lap", specifier = ">=0.5.12" }, + { name = "moondream" }, + { name = "ollama", specifier = ">=0.6.0" }, + { name = "open-clip-torch", specifier = "==3.2.0" }, + { name = "openai" }, + { name = "tensorboard", specifier = "==2.20.0" }, + { name = "torch" }, + { name = "torchreid", specifier = "==0.2.5" }, + { name = "transformers", extras = ["torch"], specifier = "==4.53.3" }, + { name = "ultralytics", specifier = ">=8.3.70" }, + { name = "xacro" }, +] +tests = [ + { name = "chromadb", specifier = ">=1.0.0" }, + { name = "coverage", specifier = ">=7.0" }, + { name = "dimos", extras = ["apriltag", "mapping", "drone", "cpu", "learning", "manipulation-toppra"] }, + { name = "dimos", extras = ["web", "visualization"] }, + { name = "einops", specifier = ">=0.8.1" }, + { name = "gdown", specifier = "==6.0.0" }, + { name = "googlemaps", specifier = ">=4.10.0" }, + { name = "hydra-core", specifier = ">=1.3.0" }, + { name = "langchain", specifier = "==1.2.3" }, + { name = "langchain-core", specifier = "==1.3.3" }, + { name = "langchain-openai", specifier = ">=1,<2" }, + { name = "lap", specifier = ">=0.5.12" }, + { name = "maturin", specifier = ">=1.7" }, + { name = "md-babel-py", specifier = ">=1.2.0" }, + { name = "moondream" }, + { name = "mujoco", specifier = ">=3.3.4" }, + { name = "ollama", specifier = ">=0.6.0" }, + { name = "open-clip-torch", specifier = "==3.2.0" }, + { name = "openai" }, + { name = "pre-commit", specifier = "==4.2.0" }, + { name = "py-spy" }, + { name = "pygame", specifier = ">=2.6.1" }, + { name = "pytest", specifier = "==8.3.5" }, + { name = "pytest-asyncio", specifier = "==0.26.0" }, + { name = "pytest-cov", specifier = ">=5.0" }, + { name = "pytest-env", specifier = "==1.1.5" }, + { name = "pytest-error-for-skips", specifier = ">=2.0.2" }, + { name = "pytest-mock", specifier = "==3.15.0" }, + { name = "pytest-timeout", specifier = "==2.4.0" }, + { name = "pytest-xdist", specifier = ">=3.5.0" }, + { name = "python-can", specifier = ">=4" }, + { name = "python-lsp-ruff", specifier = "==2.3.0" }, + { name = "python-lsp-server", extras = ["all"], specifier = "==1.14.0" }, + { name = "requests-mock", specifier = "==1.12.1" }, + { name = "tensorboard", specifier = "==2.20.0" }, + { name = "torch" }, + { name = "torchreid", specifier = "==0.2.5" }, + { name = "transformers", extras = ["torch"], specifier = "==4.53.3" }, + { name = "ultralytics", specifier = ">=8.3.70" }, + { name = "unitree-webrtc-connect", specifier = ">=2.1.2" }, + { name = "viser", extras = ["urdf"], marker = "platform_machine != 'aarch64' or sys_platform != 'linux'", specifier = ">=1.0.29" }, + { name = "watchdog", specifier = ">=3.0.0" }, + { name = "xacro" }, +] +tests-self-hosted = [ + { name = "chromadb", specifier = ">=1.0.0" }, + { name = "coverage", specifier = ">=7.0" }, + { name = "dimos", extras = ["agents", "perception", "manipulation", "sim", "unitree", "misc"] }, + { name = "dimos", extras = ["apriltag", "mapping", "drone", "cpu", "learning", "manipulation-toppra"] }, + { name = "dimos", extras = ["web", "visualization"] }, + { name = "einops", specifier = ">=0.8.1" }, + { name = "gdown", specifier = "==6.0.0" }, + { name = "googlemaps", specifier = ">=4.10.0" }, + { name = "hydra-core", specifier = ">=1.3.0" }, + { name = "langchain", specifier = "==1.2.3" }, + { name = "langchain-core", specifier = "==1.3.3" }, + { name = "langchain-openai", specifier = ">=1,<2" }, + { name = "lap", specifier = ">=0.5.12" }, + { name = "maturin", specifier = ">=1.7" }, + { name = "mcap", specifier = ">=1.2.0" }, + { name = "md-babel-py", specifier = ">=1.2.0" }, + { name = "moondream" }, + { name = "mujoco", specifier = ">=3.3.4" }, + { name = "ollama", specifier = ">=0.6.0" }, + { name = "open-clip-torch", specifier = "==3.2.0" }, + { name = "openai" }, + { name = "pre-commit", specifier = "==4.2.0" }, + { name = "py-spy" }, + { name = "pybind11", specifier = ">=2.12" }, + { name = "pygame", specifier = ">=2.6.1" }, + { name = "pytest", specifier = "==8.3.5" }, + { name = "pytest-asyncio", specifier = "==0.26.0" }, + { name = "pytest-cov", specifier = ">=5.0" }, + { name = "pytest-env", specifier = "==1.1.5" }, + { name = "pytest-error-for-skips", specifier = ">=2.0.2" }, + { name = "pytest-mock", specifier = "==3.15.0" }, + { name = "pytest-timeout", specifier = "==2.4.0" }, + { name = "pytest-xdist", specifier = ">=3.5.0" }, + { name = "python-can", specifier = ">=4" }, + { name = "python-lsp-ruff", specifier = "==2.3.0" }, + { name = "python-lsp-server", extras = ["all"], specifier = "==1.14.0" }, + { name = "requests-mock", specifier = "==1.12.1" }, + { name = "tensorboard", specifier = "==2.20.0" }, + { name = "torch" }, + { name = "torchreid", specifier = "==0.2.5" }, + { name = "transformers", extras = ["torch"], specifier = "==4.53.3" }, + { name = "ultralytics", specifier = ">=8.3.70" }, + { name = "unitree-webrtc-connect", specifier = ">=2.1.2" }, + { name = "viser", extras = ["urdf"], marker = "platform_machine != 'aarch64' or sys_platform != 'linux'", specifier = ">=1.0.29" }, + { name = "watchdog", specifier = ">=3.0.0" }, + { name = "xacro" }, +] + +[[package]] +name = "dimos-lcm" +version = "0.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "foxglove-websocket" }, + { name = "lcm-dimos-fork" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/b2/4cdd2bce665ab633313b5d371e0a63fcae2cf77a934a2a9bf820db88a540/dimos_lcm-0.1.3.tar.gz", hash = "sha256:5f7dbd3055f299823bc0e450c59583ad5a2d093c182deec8a86073853881bb09", size = 405688, upload-time = "2026-06-03T07:20:20.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/19/2d8babf544993508359922ea59db2280d42efa0d5a80d4d0cae22ce40d67/dimos_lcm-0.1.3-py3-none-any.whl", hash = "sha256:63317225a0b4ab0f05e4b656f3f78ac9ba4e914998da2c689ca839cbbd83d57d", size = 1714087, upload-time = "2026-06-03T07:20:22.689Z" }, +] + +[[package]] +name = "dimos-libero-pro-sidecar" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "dimos" }, + { name = "dimos-runtime-protocol" }, + { name = "libero" }, + { name = "mujoco" }, +] + +[package.optional-dependencies] +assets = [ + { name = "huggingface-hub" }, +] + +[package.metadata] +requires-dist = [ + { name = "dimos", editable = "../../" }, + { name = "dimos-runtime-protocol", editable = "../dimos-runtime-protocol" }, + { name = "huggingface-hub", marker = "extra == 'assets'" }, + { name = "libero" }, + { name = "mujoco", specifier = "<3.3" }, +] +provides-extras = ["assets"] + +[[package]] +name = "dimos-runtime-protocol" +version = "0.1.0" +source = { editable = "../dimos-runtime-protocol" } +dependencies = [ + { name = "pydantic" }, + { name = "typing-extensions" }, +] + +[package.metadata] +requires-dist = [ + { name = "msgpack", marker = "extra == 'msgpack'", specifier = ">=1.1" }, + { name = "pydantic", specifier = ">=2.0" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8" }, + { name = "typing-extensions", specifier = ">=4.12" }, +] +provides-extras = ["msgpack", "test"] + +[[package]] +name = "dimos-viewer" +version = "0.32.0a1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/12/f5ccd760ed27c930d08289589f420e3d3e4c707cb44bc4bd85a3e7b8cd18/dimos_viewer-0.32.0a1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c0aa61c7369337af9fea5d49ad648fbee36ed50cfdf2463450a61866fc73783", size = 38326738, upload-time = "2026-05-19T14:54:55.116Z" }, + { url = "https://files.pythonhosted.org/packages/d8/fa/b1e1e0d7d125dfbcb5465af22a44de56ae892d309f07231d4a462b5d6c1b/dimos_viewer-0.32.0a1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4763823cc555069b703c980a8fa938e1c8f858f1a8acf00b6e257ac7f5be7e93", size = 42253912, upload-time = "2026-05-19T14:54:58.58Z" }, + { url = "https://files.pythonhosted.org/packages/09/d9/cccad09df71d14b84f95fb384213d0d2eb36efb25db11fafe12e3f27454e/dimos_viewer-0.32.0a1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6db249eff2014f746e7b840a9354dc57e182d8b05bee360fdc934f34c0d4a6da", size = 44794930, upload-time = "2026-05-19T14:55:02.2Z" }, + { url = "https://files.pythonhosted.org/packages/ec/37/394f74ad8981c4666d9014189f3fb154e7f796c408d770d94f52ed4b1ccb/dimos_viewer-0.32.0a1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:86f2dc0f5db732b28ce5d9f13749a12b26f3b3c891e68bae8fcb4c065f08ddf5", size = 38326731, upload-time = "2026-05-19T14:55:05.95Z" }, + { url = "https://files.pythonhosted.org/packages/35/0d/677674fd9a8ac73689642cd7c4bb1b177df9d096f39a74dadd1420ffc221/dimos_viewer-0.32.0a1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f22176cea73d6e898230432de19ca07772a7c1293171ad86dae0b7e54e1fbbf5", size = 42253900, upload-time = "2026-05-19T14:55:09.56Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/0c32f2a5e0908dcab3d8b652704d68124cbe2d74ca418561e503fa216cff/dimos_viewer-0.32.0a1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f4a4b6e1aa83b50421a7133b22c27c0924b2f984a242d30273087f66928af5d8", size = 44794937, upload-time = "2026-05-19T14:55:12.888Z" }, + { url = "https://files.pythonhosted.org/packages/3f/51/efcf47f154b5940d09d6e480f3f75f00a927396d9be1f5b2523b1ff9d62e/dimos_viewer-0.32.0a1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4cc88e26567955ce964a250c7250cd3d52e99f330d892420699f0580de8c4f3", size = 38326735, upload-time = "2026-05-19T14:55:16.477Z" }, + { url = "https://files.pythonhosted.org/packages/fa/3e/7564e12f24f4678ba5e311a538eda0a2e46add9afcd58d08984e66bc280c/dimos_viewer-0.32.0a1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:68354192271a201a71491245a7b444b0e4047b74c9d2496a395010356917c0c3", size = 42253910, upload-time = "2026-05-19T14:55:20.232Z" }, + { url = "https://files.pythonhosted.org/packages/ca/4b/d97f13e3585582c79037cd24952ac99de593a30c300ddcc8c83d9cab1128/dimos_viewer-0.32.0a1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ebdd64ce20d73e9d6e0256ef099a3d544c14b9b227aa005ba8c362052889b3c4", size = 44794790, upload-time = "2026-05-19T14:55:24.211Z" }, +] + +[[package]] +name = "easydict" +version = "1.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/9f/d18d6b5e19244788a6d09c14a8406376b4f4bfcc008e6d17a4f4c15362e8/easydict-1.13.tar.gz", hash = "sha256:b1135dedbc41c8010e2bc1f77ec9744c7faa42bce1a1c87416791449d6c87780", size = 6809, upload-time = "2024-03-04T12:04:41.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/ec/fa6963f1198172c2b75c9ab6ecefb3045991f92f75f5eb41b6621b198123/easydict-1.13-py3-none-any.whl", hash = "sha256:6b787daf4dcaf6377b4ad9403a5cee5a86adbc0ca9a5bcf5410e9902002aeac2", size = 6804, upload-time = "2024-03-04T12:04:39.508Z" }, +] + +[[package]] +name = "egl-probe" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/c3/c5fea892e18631721a7d7e492a790bde8e3dc930861866bedb38762d0d7c/egl_probe-1.0.2.tar.gz", hash = "sha256:29bdca7b08da1e060cfb42cd46af8300a7ac4f3b1b2eeb16e545ea16d9a5ac93", size = 217495, upload-time = "2021-07-16T08:50:18.481Z" } + +[[package]] +name = "eigenpy" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "cmeel-boost" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/04/15aadcaf4141c791217724c3edc103cd66c5444987cb897a2e7112bb94ee/eigenpy-3.13.0.tar.gz", hash = "sha256:49d3cb46b85696baf5445ac707b48aade07c21579c188dfc1c8ad2f92e05fab5", size = 229750, upload-time = "2026-05-21T11:01:28.805Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/37/2bd0e146617320909c3f3a1ea0d37fa0f67ad3b5207e872894c64bb65839/eigenpy-3.13.0-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c6e844454c5a615b58e9e7cab9cb3297ef23bab6e75228b0c044dde828eb64c2", size = 5440359, upload-time = "2026-05-21T11:00:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/1a/e8/aef32d79adf393f6cb65afa29eab023a9fbd4a08788bc51d6c8ad121f39c/eigenpy-3.13.0-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:56ca6e05cc563a6c42c8ba6b4cdb97b62dd24c25885bb79d310951afe064dfa0", size = 4251076, upload-time = "2026-05-21T11:00:47.296Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ed/023b4e3eb66c4e4e3ac2ff5e336e71453e1c292975a92402adc982acbcf7/eigenpy-3.13.0-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7716543b81705c4be8a3c6d11c0c9e43dbb2ef1863cb3379afc696d10b1f0a3b", size = 6229733, upload-time = "2026-05-21T11:00:49.212Z" }, + { url = "https://files.pythonhosted.org/packages/46/2e/2c379bc21214015df4723dfa22c3dc24310149d8d4395fda95156b122020/eigenpy-3.13.0-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1c294ea951cd88c256666127b39c5881f9320fcfe144d474c095246297ca2cbd", size = 6063953, upload-time = "2026-05-21T11:00:51.805Z" }, + { url = "https://files.pythonhosted.org/packages/f7/fe/20fccfbb855a2fc94cf1b05eac2634abd77f47126c3e0d3e0353665b2f2b/eigenpy-3.13.0-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbcf85629d25d822db935ab9d5415218cff0717d620f87e1f3e7b86f1357cfd4", size = 5440358, upload-time = "2026-05-21T11:00:53.805Z" }, + { url = "https://files.pythonhosted.org/packages/ee/52/fa7433226e3af2437b70bb2d8357173cae74abe3ead4240c9b136a794043/eigenpy-3.13.0-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e4d51aa0980fd7b4d47acc9c2ac9c4e9bbb6a6d1e2149ba2426292f2972276e", size = 4251073, upload-time = "2026-05-21T11:00:55.921Z" }, + { url = "https://files.pythonhosted.org/packages/66/1e/6c3e45fbd37812faf9343c70a7661f376c123259f9e43e6f2b4fc4211f5c/eigenpy-3.13.0-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:aabe2f552e6ecae5381446bc03ba612469a10a06dfc471bbf2f194e0edcd6974", size = 6228917, upload-time = "2026-05-21T11:00:58.128Z" }, + { url = "https://files.pythonhosted.org/packages/83/a3/79326cff7ab8a55cd9024eee4ddf3082ac9f7be2c978fc5f5b056affc968/eigenpy-3.13.0-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bca70df5845a3598f1ddd77a3ce46ef5eb28228c85e362e4f75115c6cb80dcff", size = 6064372, upload-time = "2026-05-21T11:01:00.376Z" }, + { url = "https://files.pythonhosted.org/packages/07/03/bfc7a04ef67dd330f7cee8c984d6d101457a19dd9612e5a15306417b62c2/eigenpy-3.13.0-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2203a3073e54a9d55b9766b2d8fdaa6eae3e733742ac5862ca8afbd4e55021ae", size = 5485488, upload-time = "2026-05-21T11:01:02.43Z" }, + { url = "https://files.pythonhosted.org/packages/ba/61/cb30323bce5cec4a5a496e41972e12110638dbdba94f2d23814dd82f0eb4/eigenpy-3.13.0-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a844167b3dcb6157cd782fe8cd0396153548eccdcfe81e95803f2499f306ed18", size = 4261367, upload-time = "2026-05-21T11:01:05.708Z" }, + { url = "https://files.pythonhosted.org/packages/fb/90/28a30554ff1a6794a2ef91ffc021737796441d617acd649efecf6b307cce/eigenpy-3.13.0-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5524d36fe0705d0097a42e0d18e4eb3f49dc1e28be301200ab7af7d58b9ae5f", size = 6230829, upload-time = "2026-05-21T11:01:07.631Z" }, + { url = "https://files.pythonhosted.org/packages/5f/39/f3f15daffdcecc31612135ae0f77603ce6ba88c9fcc1c6a5f1e53b2eb0f9/eigenpy-3.13.0-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:cde8ed4080da67e0c037ba1453c8d33c74465714ce5196781b640dcc398579a5", size = 6068314, upload-time = "2026-05-21T11:01:10.279Z" }, +] + +[[package]] +name = "einops" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/77/850bef8d72ffb9219f0b1aac23fbc1bf7d038ee6ea666f331fa273031aa2/einops-0.8.2.tar.gz", hash = "sha256:609da665570e5e265e27283aab09e7f279ade90c4f01bcfca111f3d3e13f2827", size = 56261, upload-time = "2026-01-26T04:13:17.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/09/f8d8f8f31e4483c10a906437b4ce31bdf3d6d417b73fe33f1a8b59e34228/einops-0.8.2-py3-none-any.whl", hash = "sha256:54058201ac7087911181bfec4af6091bb59380360f069276601256a76af08193", size = 65638, upload-time = "2026-01-26T04:13:18.546Z" }, +] + +[[package]] +name = "etils" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/a0/522bbff0f3cdd37968f90dd7f26c7aa801ed87f5ba335f156de7f2b88a48/etils-1.13.0.tar.gz", hash = "sha256:a5b60c71f95bcd2d43d4e9fb3dc3879120c1f60472bb5ce19f7a860b1d44f607", size = 106368, upload-time = "2025-07-15T10:29:10.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/98/87b5946356095738cb90a6df7b35ff69ac5750f6e783d5fbcc5cb3b6cbd7/etils-1.13.0-py3-none-any.whl", hash = "sha256:d9cd4f40fbe77ad6613b7348a18132cc511237b6c076dbb89105c0b520a4c6bb", size = 170603, upload-time = "2025-07-15T10:29:09.076Z" }, +] + +[package.optional-dependencies] +epath = [ + { name = "fsspec", marker = "python_full_version < '3.11'" }, + { name = "importlib-resources", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "zipp", marker = "python_full_version < '3.11'" }, +] + +[[package]] +name = "etils" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/26/ce/6e067242fde898841922ac6fc82b0bb2fe35c38e995880bdffdfbe30182a/etils-1.14.0.tar.gz", hash = "sha256:8136e7f4c4173cd0af0ca5481c4475152f0b8686192951eefa60ee8711e1ede4", size = 108127, upload-time = "2026-03-04T17:41:36.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/3d/589663aeeacd59bb2f3e8596bfd3e81cf0fb18d70bb433199041f469771b/etils-1.14.0-py3-none-any.whl", hash = "sha256:b5df7341f54dbe1405a4450b2741207b4a8c279780402b45f87202b94dfc52b4", size = 172934, upload-time = "2026-03-04T17:41:35.01Z" }, +] + +[package.optional-dependencies] +epath = [ + { name = "fsspec", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, + { name = "zipp", marker = "python_full_version >= '3.11'" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "farama-notifications" +version = "0.0.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/91/14397890dde30adc4bee6462158933806207bc5dd10d7b4d09d5c33845cf/farama_notifications-0.0.6.tar.gz", hash = "sha256:b19acac4bb41d76e59e03394b5dd165f4761c86fa327f56307a35cbee3b60158", size = 2517, upload-time = "2026-04-24T08:43:57.603Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/f0/21f81892e4ed10f4ec3ef2e7cf8635fb76e7c0907c55d0da66be50094760/farama_notifications-0.0.6-py3-none-any.whl", hash = "sha256:f84839188efa1ce5bb361c2a84881b2dc2c0d0d7fb661ff00421820170930935", size = 2897, upload-time = "2026-04-24T08:43:56.785Z" }, +] + +[[package]] +name = "fastjsonschema" +version = "2.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/dc/be6cbe99670cd6e4ad387123647cb08e0c32975e223f82551e914c5568a6/filelock-3.29.4.tar.gz", hash = "sha256:10cdb3656fc44541cdf30652a93fb10ec6b05325620eb316bd26893e4201538a", size = 63028, upload-time = "2026-06-13T16:12:00.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/37/a065dc3bd6e49423a6532c642ca7378d3f467b1ef44c2800c937af7f9739/filelock-3.29.4-py3-none-any.whl", hash = "sha256:dac1648087d5115554850d113e7dd8c83ab2d38e3435dde2d4f163847e57b767", size = 42757, upload-time = "2026-06-13T16:11:59.582Z" }, +] + +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "fonttools" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/c9/4141c90a90db20f807c7e10bfd689fe53eb8f7f4caff58ee4d4dfe46919f/fonttools-4.63.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e3297a6a4059b4acc3a1e9a8b04741f240a80044eef08ebd32e8b5bcdddce75b", size = 2884632, upload-time = "2026-05-14T12:02:38.56Z" }, + { url = "https://files.pythonhosted.org/packages/b8/46/ad12b5c10eae602d7ef814b02afa08aacbf89da917fed5b071282b7eadc2/fonttools-4.63.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b1cd75a03ad8cb5bc40c90bfde68c0c47de423aa19e5c0f362b43520645eea94", size = 2429441, upload-time = "2026-05-14T12:02:41.162Z" }, + { url = "https://files.pythonhosted.org/packages/90/8f/bdca24a84c81d56fffed052229cdcff368f6e05882e526f4558891481f65/fonttools-4.63.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0425b277a59cff3d80ca42162a8de360f318438a2ac83570842a678d826d579", size = 4946346, upload-time = "2026-05-14T12:02:43.41Z" }, + { url = "https://files.pythonhosted.org/packages/04/59/a639c0e136441ee91a65b56fdf89e5d075927e7a09c559d1b0f5276577db/fonttools-4.63.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d7e5c9973aa04c95650c96e5f5ad865fbf42d62079163ecfab1e01cbc2504c22", size = 4903184, upload-time = "2026-05-14T12:02:45.742Z" }, + { url = "https://files.pythonhosted.org/packages/e6/53/91b7e0cb45b536f3da1b29ba8cbab89f27e8b986809e0b1982303a3f4eca/fonttools-4.63.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cb014d58140a38135f16064c74c652ed57aa0b75cbf8bb59cac821f7edb5334e", size = 4922967, upload-time = "2026-05-14T12:02:48.386Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b7/87439bf44e6b97c5538cd29d0b7e366a5b8ce2cc132a4134fb67fa3f2fa2/fonttools-4.63.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:032038247a96c1690f9f31e377c389383c902531b085aa4e4dabd6f57f870e69", size = 5042799, upload-time = "2026-05-14T12:02:50.424Z" }, + { url = "https://files.pythonhosted.org/packages/ad/7c/8b96c3263b89ef99cded544c0f0636686f85dbd3c211c4dceef0231fca23/fonttools-4.63.0-cp310-cp310-win32.whl", hash = "sha256:a8b33a82979e0a6a34ff435cc81317be1f95ec1ebb7a3a2d1c8a6a54f02ae44e", size = 1519704, upload-time = "2026-05-14T12:02:52.523Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4d/2c2f0069970b6907de8fb5b05c5c0193cc22f717df151d1c7aef1c738f58/fonttools-4.63.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c18358a155d75034911c5ee397a5b44cd19dd325dbb8b35fb60bf421d6a72ac", size = 1568666, upload-time = "2026-05-14T12:02:54.917Z" }, + { url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" }, + { url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" }, + { url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" }, + { url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473, upload-time = "2026-05-14T12:03:09.271Z" }, + { url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389, upload-time = "2026-05-14T12:03:11.53Z" }, + { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, + { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" }, + { 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 = "foxglove-websocket" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/b5/df32ac550eb0df9000ed78d872eb19738edecfd88f47fe08588d5066f317/foxglove_websocket-0.1.4.tar.gz", hash = "sha256:2ec8936982e478d103dd90268a572599fc0cce45a4ab95490d5bc31f7c8a8af8", size = 16616, upload-time = "2025-07-14T20:26:28.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/73/3a3e6cb864ddf98800a9236ad497d32e5b50eb1682ac659f7d669d92faec/foxglove_websocket-0.1.4-py3-none-any.whl", hash = "sha256:772e24e2c98bdfc704df53f7177c8ff5bab0abc4dac59a91463aca16debdd83a", size = 14392, upload-time = "2025-07-14T20:26:26.899Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646, upload-time = "2026-06-16T01:57:28.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949, upload-time = "2026-06-16T01:57:26.358Z" }, +] + +[[package]] +name = "future" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/b2/4140c69c6a66432916b26158687e821ba631a4c9273c474343badf84d3ba/future-1.0.0.tar.gz", hash = "sha256:bd2968309307861edae1458a4f8a4f3598c03be43b97521076aebf5d94c07b05", size = 1228490, upload-time = "2024-02-21T11:52:38.461Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl", hash = "sha256:929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216", size = 491326, upload-time = "2024-02-21T11:52:35.956Z" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.1.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc", size = 219798, upload-time = "2026-05-06T04:01:26.571Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, +] + +[[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.81.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b0/b5/1ff353970a87eda4c98251e34d2dfd214abd4982dc89119c9252a2a482d2/grpcio-1.81.1.tar.gz", hash = "sha256:6fa10a767143a5e82e8eaab53918af0cd8909a57a27f8cb2288b80a613ac671b", size = 13026582, upload-time = "2026-06-11T12:46:51.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/d5/f2b159d8eec08be2a855ef698f5b6f7f9fdda022e4dd9e4f5d968affd678/grpcio-1.81.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:6f9a0c9c1cc15c112d1c053064fd032b64917062292c3d70aea280e02ae10b77", size = 6086868, upload-time = "2026-06-11T12:44:19.364Z" }, + { url = "https://files.pythonhosted.org/packages/80/41/9c95232b94b219ed8b14029d9cd000e0381cafba869c451dda60af84f4ba/grpcio-1.81.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:69ef28e54fc85397f91b8c19592b8ef3d81952080366914823bd8572a2958120", size = 12062291, upload-time = "2026-06-11T12:44:27.142Z" }, + { url = "https://files.pythonhosted.org/packages/83/8b/bd9284bdd665ddf877a3e8bc2930d1bcf6ebdbae7b0da5c783dc26bd6e33/grpcio-1.81.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:15641444eca4a29358107b3dceb74c1c6305c55c822fd199b458aaea4068a7fb", size = 6635242, upload-time = "2026-06-11T12:44:30.741Z" }, + { url = "https://files.pythonhosted.org/packages/60/24/78fa025517a925f1a17da71c4ef9d5f1c6f9fa65af22dfb523c5c6317a21/grpcio-1.81.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:d4b2dddfc219f54f956ccd53cf76a1d338ffe68fc7f2849ec9c7feb9927ff692", size = 7332974, upload-time = "2026-06-11T12:44:33.72Z" }, + { url = "https://files.pythonhosted.org/packages/f7/11/402295b388dd35861007f8a26a37c2e2f284212d57bdf407c31f36043746/grpcio-1.81.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca1cc11d82677b9662082e5478b7528e2b7db7beaa6bdff42bd62789d81be399", size = 6836597, upload-time = "2026-06-11T12:44:36.108Z" }, + { url = "https://files.pythonhosted.org/packages/4d/71/37b10fd4fd579ffade6e695c14e9df5e8cba9e2365b81c131da438b67c34/grpcio-1.81.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa2ba7d2ad6df4d80127cea65e5b8d5e2c3adbf153ff4804452836328aca7c54", size = 7440660, upload-time = "2026-06-11T12:44:38.664Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d5/40203f828abc83d458b634666df6df13778032f178c03845ad5a93682388/grpcio-1.81.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:592b5fee597faa91cce2dd294dd7d9a1c83d76c4dbf877e33ec1adb866b2fbed", size = 8443171, upload-time = "2026-06-11T12:44:41.678Z" }, + { url = "https://files.pythonhosted.org/packages/eb/2c/0ed82ea35b5ec595e10444940c1db8c0e0ef57aa46bc8797d5ff838a219e/grpcio-1.81.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62481553b1793a27e9b9c3cf9e5bd483ef045ca72462592074b46d42b0c4d9b9", size = 7868905, upload-time = "2026-06-11T12:44:44.854Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1f/dcbdc1a68a07cc2b631c3098953794f17d75f93426a019240b90ce5423d6/grpcio-1.81.1-cp310-cp310-win32.whl", hash = "sha256:bb693b1e3d9a2f3fd228e2110daf4b5aeedb36761ca1e4282f74725f6d89f611", size = 4202215, upload-time = "2026-06-11T12:44:47.165Z" }, + { url = "https://files.pythonhosted.org/packages/75/a1/d7ab9f1f42efcb7d9e6111d38be6b367737a72ea2c534e1f55c81e1b6436/grpcio-1.81.1-cp310-cp310-win_amd64.whl", hash = "sha256:88268ca418cacea64cecb0d1d600d3c6b3a8038fcba02e1e205178c5b1f47661", size = 4936582, upload-time = "2026-06-11T12:44:49.479Z" }, + { url = "https://files.pythonhosted.org/packages/52/ea/1c2fa386b718ff493225e61cfc052ef400b4d6ffc54cbe261026432624b5/grpcio-1.81.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:d71d30f2d92f67d944631c523713934fee37292469e182ebcd2c1dd8a64ce53f", size = 6093112, upload-time = "2026-06-11T12:44:52.131Z" }, + { url = "https://files.pythonhosted.org/packages/2b/18/acf45fa8bd1bc5d7b0c2fd3dc4c209379fbd5bb396b440b68a83342226b7/grpcio-1.81.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b137f4bf3ada9dc44d411478decc6ff09a79ed30b306cd2abaa98408c3588137", size = 12074277, upload-time = "2026-06-11T12:44:55.354Z" }, + { url = "https://files.pythonhosted.org/packages/48/d7/ee86a60699b7db039f772a2c4a7e4facc7138984ff42c0130933a0063884/grpcio-1.81.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a3acb384427816dd5d470f47e62137b87f74da694faa8a50147012cf40df276a", size = 6640348, upload-time = "2026-06-11T12:44:59.223Z" }, + { url = "https://files.pythonhosted.org/packages/26/ee/d2de5e47378ffc207d476c230fea3be4d2601edbce9995f4fe45535d4896/grpcio-1.81.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f9a0ebbe45c29b5e5866593c12b78bd9035f0f0f0d4bc8361680cd580d99db49", size = 7331842, upload-time = "2026-06-11T12:45:02.001Z" }, + { url = "https://files.pythonhosted.org/packages/23/d6/abeda5c2b896a0b341584fe5ac411bbf72e197a9a374c355fb90965e08d2/grpcio-1.81.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a37165cc80b1a368384b383e63a4c38116a10467ae44c904d2d7468c4470ec2", size = 6842229, upload-time = "2026-06-11T12:45:04.76Z" }, + { url = "https://files.pythonhosted.org/packages/10/1c/1f0da7d590b4aeee006826ba568d0e419ca14b23e18f901a3da3e9fba613/grpcio-1.81.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6282caffb41ec326d4cb67ca9cf53b739d1b2f975a2acb498c7418e9f7d9a416", size = 7446096, upload-time = "2026-06-11T12:45:07.499Z" }, + { url = "https://files.pythonhosted.org/packages/6a/81/5c505d508f7c887aa7982d21443a4126597c80d34b0bcf40f9cec576d7f3/grpcio-1.81.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a35009284d0d3d5c2c9601c164a911b8b4331608d98a9a66d47d97bb2f522b70", size = 8445238, upload-time = "2026-06-11T12:45:10.243Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b2/524847365122ee509ca17bcc4e092198b700e94af7bfd5bb5e6dd9f3ee66/grpcio-1.81.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1b22c80559854b789a01fd89e8929b3798a156c0829b5282a8939f33ad4115ad", size = 7873989, upload-time = "2026-06-11T12:45:13.102Z" }, + { url = "https://files.pythonhosted.org/packages/18/fa/07c037c50b006909d1d13a5848774f8aa7b242f70dc03a035c64eea0e6db/grpcio-1.81.1-cp311-cp311-win32.whl", hash = "sha256:428bec0161b48d8cf583c068591bc0016d0d9cfff52462b72b3884861ea768c5", size = 4202223, upload-time = "2026-06-11T12:45:16.166Z" }, + { url = "https://files.pythonhosted.org/packages/41/ed/6bff15376920942fac6b95b9802752b837437172c9e8fc2d3170546b89cc/grpcio-1.81.1-cp311-cp311-win_amd64.whl", hash = "sha256:30e825f6848d9f18bba350ed6c75c1b02a0b5184474a31db9a32b1fa66fd8c79", size = 4941303, upload-time = "2026-06-11T12:45:18.724Z" }, + { url = "https://files.pythonhosted.org/packages/85/07/9a979c81738863a738dc23d65177056e71fbb2db817740ed870b33434e7a/grpcio-1.81.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:8b39472beafc0bdcafc4c8c73ad082ebfdb449d566897a61e7acb4fa88089115", size = 6053264, upload-time = "2026-06-11T12:45:21.017Z" }, + { url = "https://files.pythonhosted.org/packages/75/95/539706ca0d3bd40dbad583dc56fd883da941f37556b629132da5762781b9/grpcio-1.81.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:12b7524c88d4026d3dcb7b0ebe16b6714f3b4af402ddd0f0639ab064a00c87c3", size = 12052560, upload-time = "2026-06-11T12:45:23.652Z" }, + { url = "https://files.pythonhosted.org/packages/e0/44/f257b7e0bd69c93b06c6cb8ac8d1b901ccb42bedabd83c1a4c77a71f8810/grpcio-1.81.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1e123f9b37edb8375fd74130d1f69c944bbf0a7b06761ae7211154b8759e94d2", size = 6595983, upload-time = "2026-06-11T12:45:26.963Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f3/19782aa04c960968bef8c5539329d8e3bbc3364e2e46d19eb5e5cc5e43b7/grpcio-1.81.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2c2e2ae6867c2966b8daccc836d54a13218e0007e9a490aeb81dd05be64d22d7", size = 7303455, upload-time = "2026-06-11T12:45:29.707Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8c/dea020b6d91508cd84463917a63149ec196ee7db505d032ae43fcb3303b9/grpcio-1.81.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:766bc7c9a9c340342f4c864ccbda8e78111e4751f13b895812b9c148fb79e9d0", size = 6809167, upload-time = "2026-06-11T12:45:32.52Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c7/3030dd940408083bd32cd95d634777a71605ade4887154d93e8a89244946/grpcio-1.81.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b259a04a737cb3496be0901328eb8b7552ed8df4865d8c8f1cf1bffcfc0776a3", size = 7412536, upload-time = "2026-06-11T12:45:35.403Z" }, + { url = "https://files.pythonhosted.org/packages/e0/dd/1172a9e42b168edcafefad6115346ef619a3fc02158bb170e66ced24bcdd/grpcio-1.81.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:85b10a45b8993d195c4f3ff57025b8d1e11834909ee475c403bfa60cb4caefaf", size = 8408276, upload-time = "2026-06-11T12:45:37.78Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/71437c7f3596e5246155c515852795a85a1a8d228190212432b13b97a95d/grpcio-1.81.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8ea1936c26b99999b27479853039a7f34713f56c49375ad52b38535ec93a796c", size = 7849660, upload-time = "2026-06-11T12:45:40.627Z" }, + { url = "https://files.pythonhosted.org/packages/65/40/7debc0da45d2efebafb82da75644be347497fe4ee250514b8cd3b86ae8bf/grpcio-1.81.1-cp312-cp312-win32.whl", hash = "sha256:a185a04039df6cae8648bc8ab6d6fde7bf94f7188ecf7828e76ac52eef1e41d6", size = 4185819, upload-time = "2026-06-11T12:45:43.027Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b9/8fe3ba5ed462067774ebc1f9c7f26aa7ebcc280ddd476be107153de1339e/grpcio-1.81.1-cp312-cp312-win_amd64.whl", hash = "sha256:3ad74f8bb1a18963914c5452d289422830b39459e8776ebbcd207be1fbfb1d94", size = 4930461, upload-time = "2026-06-11T12:45:45.775Z" }, +] + +[[package]] +name = "gymnasium" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle" }, + { name = "farama-notifications" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/ff/14b6880d703dfaca204490979d3254ccd280c99550798993319902873658/gymnasium-1.3.0.tar.gz", hash = "sha256:6939e86e835d6b71b6ba6bfd360487420876deafc79bfb7bacba83a7c446bcf3", size = 830646, upload-time = "2026-04-22T13:47:14.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/73/fda6a25f3beeb5e49d74330b44092b9e5a547395ccd478d1103ddcbff1fc/gymnasium-1.3.0-py3-none-any.whl", hash = "sha256:6b8c159a8540dcbcb221722d7efda24d78ebbcbc3bd2ea1c2611aa2a34471fc2", size = 953904, upload-time = "2026-04-22T13:47:12.13Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "h5py" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/6b/231413e58a787a89b316bb0d1777da3c62257e4797e09afd8d17ad3549dc/h5py-3.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e06f864bedb2c8e7c1358e6c73af48519e317457c444d6f3d332bb4e8fa6d7d9", size = 3724137, upload-time = "2026-03-06T13:47:35.242Z" }, + { url = "https://files.pythonhosted.org/packages/74/f9/557ce3aad0fe8471fb5279bab0fc56ea473858a022c4ce8a0b8f303d64e9/h5py-3.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec86d4fffd87a0f4cb3d5796ceb5a50123a2a6d99b43e616e5504e66a953eca3", size = 3090112, upload-time = "2026-03-06T13:47:37.634Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f5/e15b3d0dc8a18e56409a839e6468d6fb589bc5207c917399c2e0706eeb44/h5py-3.16.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:86385ea895508220b8a7e45efa428aeafaa586bd737c7af9ee04661d8d84a10d", size = 4844847, upload-time = "2026-03-06T13:47:39.811Z" }, + { url = "https://files.pythonhosted.org/packages/cb/92/a8851d936547efe30cc0ce5245feac01f3ec6171f7899bc3f775c72030b3/h5py-3.16.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8975273c2c5921c25700193b408e28d6bdd0111c37468b2d4e25dcec4cd1d84d", size = 5065352, upload-time = "2026-03-06T13:47:41.489Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ae/f2adc5d0ca9626db3277a3d87516e124cbc5d0eea0bd79bc085702d04f2c/h5py-3.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1677ad48b703f44efc9ea0c3ab284527f81bc4f318386aaaebc5fede6bbae56f", size = 4839173, upload-time = "2026-03-06T13:47:43.586Z" }, + { url = "https://files.pythonhosted.org/packages/64/0b/e0c8c69da1d8838da023a50cd3080eae5d475691f7636b35eff20bb6ef20/h5py-3.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c4dd4cf5f0a4e36083f73172f6cfc25a5710789269547f132a20975bfe2434c", size = 5076216, upload-time = "2026-03-06T13:47:45.315Z" }, + { url = "https://files.pythonhosted.org/packages/66/35/d88fd6718832133c885004c61ceeeb24dbd6397ef877dbed6b3a64d6a286/h5py-3.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:bdef06507725b455fccba9c16529121a5e1fbf56aa375f7d9713d9e8ff42454d", size = 3183639, upload-time = "2026-03-06T13:47:47.041Z" }, + { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663, upload-time = "2026-03-06T13:47:49.599Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630, upload-time = "2026-03-06T13:47:51.249Z" }, + { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472, upload-time = "2026-03-06T13:47:53.085Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150, upload-time = "2026-03-06T13:47:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544, upload-time = "2026-03-06T13:47:56.893Z" }, + { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013, upload-time = "2026-03-06T13:47:59.01Z" }, + { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673, upload-time = "2026-03-06T13:48:00.626Z" }, + { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834, upload-time = "2026-03-06T13:48:02.579Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604, upload-time = "2026-03-06T13:48:04.198Z" }, + { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940, upload-time = "2026-03-06T13:48:05.783Z" }, + { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852, upload-time = "2026-03-06T13:48:07.482Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250, upload-time = "2026-03-06T13:48:09.628Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108, upload-time = "2026-03-06T13:48:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216, upload-time = "2026-03-06T13:48:13.322Z" }, + { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868, upload-time = "2026-03-06T13:48:15.759Z" }, + { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286, upload-time = "2026-03-06T13:48:17.279Z" }, +] + +[[package]] +name = "hf-egl-probe" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/3e/ffad88145b342d5a972711d0b47e202f2e51c9a436d9b6bdd55f10ed893e/hf_egl_probe-1.0.2.tar.gz", hash = "sha256:0cbc6352236f5c14f681328e2e0d2cb4d101828c81aa58793dbc9a91feee5e93", size = 217668, upload-time = "2025-11-03T15:58:15.592Z" } + +[[package]] +name = "hf-xet" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/2d/57fd21d84d93efb4bd0b962383790e19dd1bc053501b4264c97903b4e83e/hf_xet-1.5.1.tar.gz", hash = "sha256:51ef4500dab3764b41135ee1381a4b62ce56fc54d4c92b719b59e597d6df5bf6", size = 876636, upload-time = "2026-06-08T23:02:53.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/d8/5e54cf37434759d1f4f2ba9b66077ff9d4c4e1f37b6bd7975da5c40d94ab/hf_xet-1.5.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6abd35c3221eff63836618ddfb954dcf84798603f71d8e33e3ed7b04acfdbe6e", size = 4077794, upload-time = "2026-06-08T23:02:40.656Z" }, + { url = "https://files.pythonhosted.org/packages/35/94/4b2ecfbad8f8b04701a23aefb62f540b9137d058b7e1dbef16a32676f0e9/hf_xet-1.5.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:94e761bbd266bf4c03cee73753916062665ce8365aa40ed321f45afcb934b41e", size = 3845354, upload-time = "2026-06-08T23:02:42.702Z" }, + { url = "https://files.pythonhosted.org/packages/de/cc/f99f4bc7295023d7bd9ebbfd51f75cc530ca262c1227666268b8208f4b77/hf_xet-1.5.1-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:892e3a3a3aecc12aded8b93cf4f9cd059282c7de0732f7d55026f3abdf474350", size = 4514864, upload-time = "2026-06-08T23:02:44.497Z" }, + { url = "https://files.pythonhosted.org/packages/cd/6e/21f7e5a2381278bd3b7b7a5a4d90038518bb6308a0c1daf5d9f8268bb178/hf_xet-1.5.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a93df2039190502835b1db8cd7e178b0b7b889fe9ab51299d5ced26e0dd879a4", size = 4303784, upload-time = "2026-06-08T23:02:46.203Z" }, + { url = "https://files.pythonhosted.org/packages/35/0e/f992bb6927ac1cb30ef74e62268f551f338bc32b2191f7c96a44c6f7283e/hf_xet-1.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0c97106032ef70467b4f6bc2d0ccc266d7613ee076afc56516c502f87ce1c4a6", size = 4500703, upload-time = "2026-06-08T23:02:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d1/90a498d05447980b977b1669246eeeeae4cfb0ea3e7a286eaba627f91bf9/hf_xet-1.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6208adb15d192b90e4c2ad2a27ed864359b2cb0f2494eb6d7c7f3699ac02e2bf", size = 4719498, upload-time = "2026-06-08T23:02:49.268Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b6/20f99cfe97cc663a711f7b33cc21d4793e51968e9a26125b4afcd77315ba/hf_xet-1.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:f7b3002f95d1c13e24bcb4537baa8f0eb3838957067c91bb4959bc004a6435f5", size = 4026419, upload-time = "2026-06-08T23:02:50.829Z" }, + { url = "https://files.pythonhosted.org/packages/f9/fa/77453694888f03e5a8c8852d1514a0894d8e81c622d39edbaf308ea0dcf4/hf_xet-1.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:93d090b57b211133f6c0dab0205ef5cb6d89162979ba75a74845045cc3063b8e", size = 3855178, upload-time = "2026-06-08T23:02:52.452Z" }, +] + +[[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" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +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" } +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" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/77/ce3331f40cb2d021fe9b24c46c41e72faf74493621138e5eddac12bf5e1c/huggingface_hub-1.21.0.tar.gz", hash = "sha256:a44f222cd8f2f7c2eade30b5e7a04cac984a3235fa61ea87a0a5a31db77d561f", size = 861572, upload-time = "2026-06-25T13:09:26.356Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/85/b505a99a133d9f99d21af182af416e9baef70bdeef019983479651e494c2/huggingface_hub-1.21.0-py3-none-any.whl", hash = "sha256:eadaa3678c512c82aea69e8675d90a184861e68de32f1105668628b4dce0e7cd", size = 721078, upload-time = "2026-06-25T13:09:24.402Z" }, +] + +[[package]] +name = "hydra-core" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime" }, + { name = "omegaconf" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/0b/7c0d941311aadc6479ec01767edba9c8a07db1452685de3567ed3058d0c9/hydra_core-1.3.3.tar.gz", hash = "sha256:b7477ee21f08b62f71bf0126d44695c048dc7e9c0cc79e2d593b707cb1e44048", size = 3262532, upload-time = "2026-06-11T05:54:26.835Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/57/4e39f85347f77144d2ad12e87d5df8fb8f17023f9bd9e8c6e903a128382c/hydra_core-1.3.3-py3-none-any.whl", hash = "sha256:cf349fc393f486f250e5825592c3d0a50c0af3effd726cf8dd5b637a7cb464e3", size = 154706, upload-time = "2026-06-11T05:54:24.917Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "imageio" +version = "2.37.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { 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" } +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" }, +] + +[[package]] +name = "imageio-ffmpeg" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/bd/c3343c721f2a1b0c9fc71c1aebf1966a3b7f08c2eea8ed5437a2865611d6/imageio_ffmpeg-0.6.0.tar.gz", hash = "sha256:e2556bed8e005564a9f925bb7afa4002d82770d6b08825078b7697ab88ba1755", size = 25210, upload-time = "2025-01-16T21:34:32.747Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/58/87ef68ac83f4c7690961bce288fd8e382bc5f1513860fc7f90a9c1c1c6bf/imageio_ffmpeg-0.6.0-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.whl", hash = "sha256:9d2baaf867088508d4a3458e61eeb30e945c4ad8016025545f66c4b5aaef0a61", size = 24932969, upload-time = "2025-01-16T21:34:20.464Z" }, + { url = "https://files.pythonhosted.org/packages/40/5c/f3d8a657d362cc93b81aab8feda487317da5b5d31c0e1fdfd5e986e55d17/imageio_ffmpeg-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b1ae3173414b5fc5f538a726c4e48ea97edc0d2cdc11f103afee655c463fa742", size = 21113891, upload-time = "2025-01-16T21:34:00.277Z" }, + { url = "https://files.pythonhosted.org/packages/33/e7/1925bfbc563c39c1d2e82501d8372734a5c725e53ac3b31b4c2d081e895b/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1d47bebd83d2c5fc770720d211855f208af8a596c82d17730aa51e815cdee6dc", size = 25632706, upload-time = "2025-01-16T21:33:53.475Z" }, + { url = "https://files.pythonhosted.org/packages/a0/2d/43c8522a2038e9d0e7dbdf3a61195ecc31ca576fb1527a528c877e87d973/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c7e46fcec401dd990405049d2e2f475e2b397779df2519b544b8aab515195282", size = 29498237, upload-time = "2025-01-16T21:34:13.726Z" }, + { url = "https://files.pythonhosted.org/packages/a0/13/59da54728351883c3c1d9fca1710ab8eee82c7beba585df8f25ca925f08f/imageio_ffmpeg-0.6.0-py3-none-win32.whl", hash = "sha256:196faa79366b4a82f95c0f4053191d2013f4714a715780f0ad2a68ff37483cc2", size = 19652251, upload-time = "2025-01-16T21:34:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c6/fa760e12a2483469e2bf5058c5faff664acf66cadb4df2ad6205b016a73d/imageio_ffmpeg-0.6.0-py3-none-win_amd64.whl", hash = "sha256:02fa47c83703c37df6bfe4896aab339013f62bf02c5ebf2dce6da56af04ffc0a", size = 31246824, upload-time = "2025-01-16T21:34:28.6Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/3d/2d244233ac4f76e38533cfcb2991c9eb4c7bf688ae0a036d30725b8faafe/importlib_metadata-9.0.0-py3-none-any.whl", hash = "sha256:2d21d1cc5a017bd0559e36150c21c830ab1dc304dedd1b7ea85d20f45ef3edd7", size = 27789, upload-time = "2026-03-20T06:42:55.665Z" }, +] + +[[package]] +name = "importlib-resources" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/06/b56dfa750b44e86157093bc8fca0ab81dccbf5260510de4eaf1cb69b5b99/importlib_resources-7.1.0.tar.gz", hash = "sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708", size = 44985, upload-time = "2026-04-12T16:36:09.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl", hash = "sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1", size = 37232, upload-time = "2026-04-12T16:36:08.219Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "janus" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/7f/69884b6618be4baf6ebcacc716ee8680a842428a19f403db6d1c0bb990aa/janus-2.0.0.tar.gz", hash = "sha256:0970f38e0e725400496c834a368a67ee551dc3b5ad0a257e132f5b46f2e77770", size = 22910, upload-time = "2024-12-13T12:59:08.622Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/34/65604740edcb20e1bda6a890348ed7d282e7dd23aa00401cbe36fd0edbd9/janus-2.0.0-py3-none-any.whl", hash = "sha256:7e6449d34eab04cd016befbd7d8c0d8acaaaab67cb59e076a69149f9031745f9", size = 12161, upload-time = "2024-12-13T12:59:06.106Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +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 = "jupyter-core" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, +] + +[[package]] +name = "jupytext" +version = "1.19.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/52/e014296ac8f40ca783aeb73dae52e65edbb0eaae0dcdc1ea41bfaa8aebf7/jupytext-1.19.4.tar.gz", hash = "sha256:739bcd4bc12aa4fe298a38017cdb5ae27b08a6ba3a5470728d2fe9e04b155db1", size = 4581977, upload-time = "2026-06-21T21:48:58.32Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/e9/e2ae007456069dbe01865c69a4203a7ada6f7e337b78fc2f12e51bd3fae7/jupytext-1.19.4-py3-none-any.whl", hash = "sha256:032d4ef4bd2e96addcac780b9b1d6b5a266ca39beceaaca95bfb4f06e0b77029", size = 170889, upload-time = "2026-06-21T21:48:56.352Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", size = 122802, upload-time = "2026-03-09T13:12:37.515Z" }, + { url = "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", size = 66216, upload-time = "2026-03-09T13:12:38.812Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", size = 63917, upload-time = "2026-03-09T13:12:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", size = 1628776, upload-time = "2026-03-09T13:12:41.976Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", size = 1228164, upload-time = "2026-03-09T13:12:44.002Z" }, + { url = "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", size = 1246656, upload-time = "2026-03-09T13:12:45.557Z" }, + { url = "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", size = 1295562, upload-time = "2026-03-09T13:12:47.562Z" }, + { url = "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", size = 2178473, upload-time = "2026-03-09T13:12:50.254Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", size = 2274035, upload-time = "2026-03-09T13:12:51.785Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", size = 2443217, upload-time = "2026-03-09T13:12:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", size = 2249196, upload-time = "2026-03-09T13:12:55.057Z" }, + { url = "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", size = 73389, upload-time = "2026-03-09T13:12:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", size = 64782, upload-time = "2026-03-09T13:12:57.609Z" }, + { url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" }, + { url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" }, + { url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" }, + { url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" }, + { url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" }, + { url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" }, + { url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" }, + { url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, + { url = "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", size = 59606, upload-time = "2026-03-09T13:15:40.81Z" }, + { url = "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", size = 57537, upload-time = "2026-03-09T13:15:42.071Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", size = 79888, upload-time = "2026-03-09T13:15:43.317Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", size = 77584, upload-time = "2026-03-09T13:15:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", size = 73390, upload-time = "2026-03-09T13:15:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" }, + { url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" }, + { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, +] + +[[package]] +name = "lcm-dimos-fork" +version = "1.5.2.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/53/26e5c821858fff27ed9bf5be6e848a63db89bcd6a0c4cb8546dce3396a7d/lcm_dimos_fork-1.5.2.post1.tar.gz", hash = "sha256:3ec3703605ea1ea2f82ab327d73eb7fddf3775c1365e4a112314e11f389ad72f", size = 5538346, upload-time = "2026-06-03T07:09:31.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/59/81490fca92a3df85dc5bf656f98fdfd91b1f0f489abe806057e71c87cb53/lcm_dimos_fork-1.5.2.post1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:78f8abba25e0a48d6c6e616064cf60336de730838819d5fdd97c5464705c905f", size = 3501132, upload-time = "2026-06-03T07:09:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/41/0d/8ab9a6e2894f02e1deaa49640d8a108a0ed04f9ef273691f7551da40d5e2/lcm_dimos_fork-1.5.2.post1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d96406f6d7dede356e9abb41d99191eed2dbf0ac50a1b8276e254bcf6c747c91", size = 2861833, upload-time = "2026-06-03T07:09:28.212Z" }, + { url = "https://files.pythonhosted.org/packages/39/0f/fc280fd9a2e9eb5ebd40ad8631715d251f6471be3c1c8cae5ef068a4849c/lcm_dimos_fork-1.5.2.post1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d4b59cbc8314e7dd0c92f9efd72e070b4a95cb3f7316aa63745b3b5fad80e71a", size = 2882418, upload-time = "2026-06-03T07:09:21.198Z" }, + { url = "https://files.pythonhosted.org/packages/45/50/8c640088b7722d11d6c1acec133afa44e344cbb5c25236f3ca9b8bdb0d4e/lcm_dimos_fork-1.5.2.post1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:e36c02538a5f4f3b7c4bf9cf7f130001deb4087e6c4ab2b87a92b63b5800eaf5", size = 3501233, upload-time = "2026-06-03T07:09:18.794Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e3/c2f4e3d67129caf105f9dade2c0a77755ea1d11e0bf13eb6041e8bf69954/lcm_dimos_fork-1.5.2.post1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:ca37f8bda7733084e3a840ca71df139413fa9d7187ef39eb31425301fc8effaa", size = 2861890, upload-time = "2026-06-03T07:09:16.256Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9d/f19698bc9888c0e7d1244b02e20cb4f4fa83617f046760e7f2f21799008b/lcm_dimos_fork-1.5.2.post1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:3ad82221708a7510edd7ea484733efe40c82bdb553816679b4cc0d8967d1b9be", size = 2882420, upload-time = "2026-06-03T07:09:09.346Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f8/0e300ecb1f5719a42adaadd009c27b6543576172e55f5070c8eec3e6093e/lcm_dimos_fork-1.5.2.post1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:75bd1fc3d6365bb47d1ee95c0b5dd267ca5516b393fcbc393e8c35bfb22c34b2", size = 3501253, upload-time = "2026-06-03T07:09:11.773Z" }, + { url = "https://files.pythonhosted.org/packages/8d/bf/1673d451d032cff65214692e2431858800e4baecee32f68a9ccfe19919b8/lcm_dimos_fork-1.5.2.post1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c3c4827d7d9d618270b70c564dacbc142dbce5dc03566510ba698bfebf2830b0", size = 2861845, upload-time = "2026-06-03T07:09:14.2Z" }, + { url = "https://files.pythonhosted.org/packages/87/3a/a31d94a20c3ac5d6fd3fa89b2de66dbf6a7f01551fdff29d9a5fbf02cba6/lcm_dimos_fork-1.5.2.post1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:700294567efed85e96f2ae435b7c55d8702e15f3d06154f56216c1be5c85b322", size = 2882574, upload-time = "2026-06-03T07:09:23.407Z" }, +] + +[[package]] +name = "libcoal" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "cmeel-assimp" }, + { name = "cmeel-boost" }, + { name = "cmeel-octomap" }, + { name = "cmeel-qhull" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/44/d59b10af87165def48bf55fcb3f0591274a39322d48cb92d608d17ed86b5/libcoal-3.0.3.tar.gz", hash = "sha256:fd0f887682c73ef2d429caa09721993acb596b9e529c4f37761fe34faa7675ed", size = 1464501, upload-time = "2026-05-21T08:41:47.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/35/853c33314d1dcb50ff8c1d832cbe1533dd4d0cf6be685b96bbb1d5219d35/libcoal-3.0.3-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:c1b3a0c4d10a690c61fb47eb42aee32bad827846bc7413238364ab340b1faf84", size = 1683905, upload-time = "2026-05-21T08:41:39.006Z" }, + { url = "https://files.pythonhosted.org/packages/14/bb/c2288420bd28f563ff6557f17d330fa07d6e5c5198b130da7efee9e53b86/libcoal-3.0.3-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c09ff80ff486f00bdcde1fbde14a143933d20c2c46f8567d6f95817339242fbf", size = 1484193, upload-time = "2026-05-21T08:41:40.917Z" }, + { url = "https://files.pythonhosted.org/packages/ce/60/d8ef12e6a3a21d40d146ccea38fc29e547d884ebd9f531105902b5408092/libcoal-3.0.3-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:18e203ed53da8b96b192a74c4c041fe33f98b0249bb7ec27a3b67397ce1ecc4d", size = 2257010, upload-time = "2026-05-21T08:41:42.922Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a1/062d7d444eb9260244cdced41384933f8cdf5b383ab9ed2810d048720bd3/libcoal-3.0.3-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:28fa1473d80728994f7275b8c8797858959ab77a643f07f2222feb6de155bb8e", size = 2285044, upload-time = "2026-05-21T08:41:45.03Z" }, +] + +[[package]] +name = "libero" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bddl" }, + { name = "cloudpickle" }, + { name = "easydict" }, + { name = "einops" }, + { name = "future" }, + { name = "gymnasium" }, + { name = "hf-egl-probe" }, + { name = "hydra-core" }, + { name = "matplotlib", version = "3.10.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "mujoco" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "opencv-python" }, + { name = "robomimic" }, + { name = "robosuite" }, + { name = "thop" }, + { name = "transformers" }, + { name = "wandb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/7a/5f0ec2fb6013e55fbf6a7fb3b29b98bc7b9a7f1fee6ecdfcb9b1d60225fb/libero-0.1.1.tar.gz", hash = "sha256:31f17f69ced68e2ecde630353dda924372b0b7b647991e21eade231b241fc373", size = 2963467, upload-time = "2025-11-03T09:51:48.589Z" } + +[[package]] +name = "libpinocchio" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "cmeel-boost" }, + { name = "cmeel-urdfdom" }, + { name = "libcoal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/c9/6c1d428cc8dc10eda45726ac4898bd9ce659366c934e2c37e8e5ba3ae330/libpinocchio-4.0.0.tar.gz", hash = "sha256:425a4ea81fa046238ddb7d8e0c6109bff69a80dd945f84dc3813243b59e3d548", size = 4418773, upload-time = "2026-05-21T13:30:05.593Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/f4/53010f1cf23def730d774b9a99d33ffe76da1bdb63ba2e877432d7f652bf/libpinocchio-4.0.0-0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:c497750021075ca95715d478314970a82d7b9d0ce0e9138800378db8cbd4ac3d", size = 3677932, upload-time = "2026-05-21T13:29:58.474Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/150954766542d00e9c3ede6ea918f9ec852e7657637e5dc0c842ff059339/libpinocchio-4.0.0-0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6d617a36b0a0c60774b321f1662d86aeb7161cf88581ae5c2ca4665d765ea16", size = 3116217, upload-time = "2026-05-21T13:30:00.663Z" }, + { url = "https://files.pythonhosted.org/packages/45/65/150b7d6b3986a63d20f95c0f066fadb586fed21ab027bf4cbe8a70230474/libpinocchio-4.0.0-0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:43d0ee81449126c6f1b184111cac74d83e8903a57401b8d37bde53ed946cf24e", size = 3639624, upload-time = "2026-05-21T13:30:02.244Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b4/9292b05386d51975ffe69ebcc9dee351ee8018eb0dd1acbcdf48352b2b63/libpinocchio-4.0.0-0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a5b7b8d968f1fe8a57e01fb05464521c09a8d368d2b4e482bee25db018bbd197", size = 3804644, upload-time = "2026-05-21T13:30:04.052Z" }, +] + +[[package]] +name = "linkify-it-py" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "uc-micro-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/c9/06ea13676ef354f0af6169587ae292d3e2406e212876a413bf9eece4eb23/linkify_it_py-2.1.0.tar.gz", hash = "sha256:43360231720999c10e9328dc3691160e27a718e280673d444c38d7d3aaa3b98b", size = 29158, upload-time = "2026-03-01T07:48:47.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" }, +] + +[[package]] +name = "llvmlite" +version = "0.47.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614, upload-time = "2026-03-31T18:29:53.497Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/f5/a1bde3aa8c43524b0acaf3f72fb3d80a32dd29dbb42d7dc434f84584cdcc/llvmlite-0.47.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41270b0b1310717f717cf6f2a9c68d3c43bd7905c33f003825aebc361d0d1b17", size = 37232772, upload-time = "2026-03-31T18:28:12.198Z" }, + { url = "https://files.pythonhosted.org/packages/7c/fb/76d88fc05ee1f9c1a6efe39eb493c4a727e5d1690412469017cd23bcb776/llvmlite-0.47.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f9d118bc1dd7623e0e65ca9ac485ec6dd543c3b77bc9928ddc45ebd34e1e30a7", size = 56275179, upload-time = "2026-03-31T18:28:15.725Z" }, + { url = "https://files.pythonhosted.org/packages/4d/08/29da7f36217abd56a0c389ef9a18bea47960826e691ced1a36c92c6ce93c/llvmlite-0.47.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ea5cfb04a6ab5b18e46be72b41b015975ba5980c4ddb41f1975b83e19031063", size = 55128632, upload-time = "2026-03-31T18:28:19.946Z" }, + { url = "https://files.pythonhosted.org/packages/df/f8/5e12e9ed447d65f04acf6fcf2d79cded2355640b5131a46cee4c99a5949d/llvmlite-0.47.0-cp310-cp310-win_amd64.whl", hash = "sha256:166b896a2262a2039d5fc52df5ee1659bd1ccd081183df7a2fba1b74702dd5ea", size = 38138402, upload-time = "2026-03-31T18:28:23.327Z" }, + { url = "https://files.pythonhosted.org/packages/34/0b/b9d1911cfefa61399821dfb37f486d83e0f42630a8d12f7194270c417002/llvmlite-0.47.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74090f0dcfd6f24ebbef3f21f11e38111c4d7e6919b54c4416e1e357c3446b07", size = 37232770, upload-time = "2026-03-31T18:28:26.765Z" }, + { url = "https://files.pythonhosted.org/packages/46/27/5799b020e4cdfb25a7c951c06a96397c135efcdc21b78d853bbd9c814c7d/llvmlite-0.47.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ca14f02e29134e837982497959a8e2193d6035235de1cb41a9cb2bd6da4eedbb", size = 56275177, upload-time = "2026-03-31T18:28:31.01Z" }, + { url = "https://files.pythonhosted.org/packages/7e/51/48a53fedf01cb1f3f43ef200be17ebf83c8d9a04018d3783c1a226c342c2/llvmlite-0.47.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12a69d4bb05f402f30477e21eeabe81911e7c251cecb192bed82cd83c9db10d8", size = 55128631, upload-time = "2026-03-31T18:28:36.046Z" }, + { url = "https://files.pythonhosted.org/packages/a2/50/59227d06bdc96e23322713c381af4e77420949d8cd8a042c79e0043096cc/llvmlite-0.47.0-cp311-cp311-win_amd64.whl", hash = "sha256:c37d6eb7aaabfa83ab9c2ff5b5cdb95a5e6830403937b2c588b7490724e05327", size = 38138400, upload-time = "2026-03-31T18:28:40.076Z" }, + { url = "https://files.pythonhosted.org/packages/fa/48/4b7fe0e34c169fa2f12532916133e0b219d2823b540733651b34fdac509a/llvmlite-0.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f", size = 37232769, upload-time = "2026-03-31T18:28:43.735Z" }, + { url = "https://files.pythonhosted.org/packages/e6/4b/e3f2cd17822cf772a4a51a0a8080b0032e6d37b2dbe8cfb724eac4e31c52/llvmlite-0.47.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5853bf26160857c0c2573415ff4efe01c4c651e59e2c55c2a088740acfee51cd", size = 56275178, upload-time = "2026-03-31T18:28:48.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/55/a3b4a543185305a9bdf3d9759d53646ed96e55e7dfd43f53e7a421b8fbae/llvmlite-0.47.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:003bcf7fa579e14db59c1a1e113f93ab8a06b56a4be31c7f08264d1d4072d077", size = 55128632, upload-time = "2026-03-31T18:28:52.901Z" }, + { url = "https://files.pythonhosted.org/packages/2f/f5/d281ae0f79378a5a91f308ea9fdb9f9cc068fddd09629edc0725a5a8fde1/llvmlite-0.47.0-cp312-cp312-win_amd64.whl", hash = "sha256:f3079f25bdc24cd9d27c4b2b5e68f5f60c4fdb7e8ad5ee2b9b006007558f9df7", size = 38138692, upload-time = "2026-03-31T18:28:57.147Z" }, +] + +[[package]] +name = "lz4" +version = "4.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/51/f1b86d93029f418033dddf9b9f79c8d2641e7454080478ee2aab5123173e/lz4-4.4.5.tar.gz", hash = "sha256:5f0b9e53c1e82e88c10d7c180069363980136b9d7a8306c4dca4f760d60c39f0", size = 172886, upload-time = "2025-11-03T13:02:36.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/45/2466d73d79e3940cad4b26761f356f19fd33f4409c96f100e01a5c566909/lz4-4.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d221fa421b389ab2345640a508db57da36947a437dfe31aeddb8d5c7b646c22d", size = 207396, upload-time = "2025-11-03T13:01:24.965Z" }, + { url = "https://files.pythonhosted.org/packages/72/12/7da96077a7e8918a5a57a25f1254edaf76aefb457666fcc1066deeecd609/lz4-4.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dc1e1e2dbd872f8fae529acd5e4839efd0b141eaa8ae7ce835a9fe80fbad89f", size = 207154, upload-time = "2025-11-03T13:01:26.922Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0e/0fb54f84fd1890d4af5bc0a3c1fa69678451c1a6bd40de26ec0561bb4ec5/lz4-4.4.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e928ec2d84dc8d13285b4a9288fd6246c5cde4f5f935b479f50d986911f085e3", size = 1291053, upload-time = "2025-11-03T13:01:28.396Z" }, + { url = "https://files.pythonhosted.org/packages/15/45/8ce01cc2715a19c9e72b0e423262072c17d581a8da56e0bd4550f3d76a79/lz4-4.4.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daffa4807ef54b927451208f5f85750c545a4abbff03d740835fc444cd97f758", size = 1278586, upload-time = "2025-11-03T13:01:29.906Z" }, + { url = "https://files.pythonhosted.org/packages/6d/34/7be9b09015e18510a09b8d76c304d505a7cbc66b775ec0b8f61442316818/lz4-4.4.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a2b7504d2dffed3fd19d4085fe1cc30cf221263fd01030819bdd8d2bb101cf1", size = 1367315, upload-time = "2025-11-03T13:01:31.054Z" }, + { url = "https://files.pythonhosted.org/packages/2a/94/52cc3ec0d41e8d68c985ec3b2d33631f281d8b748fb44955bc0384c2627b/lz4-4.4.5-cp310-cp310-win32.whl", hash = "sha256:0846e6e78f374156ccf21c631de80967e03cc3c01c373c665789dc0c5431e7fc", size = 88173, upload-time = "2025-11-03T13:01:32.643Z" }, + { url = "https://files.pythonhosted.org/packages/ca/35/c3c0bdc409f551404355aeeabc8da343577d0e53592368062e371a3620e1/lz4-4.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:7c4e7c44b6a31de77d4dc9772b7d2561937c9588a734681f70ec547cfbc51ecd", size = 99492, upload-time = "2025-11-03T13:01:33.813Z" }, + { url = "https://files.pythonhosted.org/packages/1d/02/4d88de2f1e97f9d05fd3d278fe412b08969bc94ff34942f5a3f09318144a/lz4-4.4.5-cp310-cp310-win_arm64.whl", hash = "sha256:15551280f5656d2206b9b43262799c89b25a25460416ec554075a8dc568e4397", size = 91280, upload-time = "2025-11-03T13:01:35.081Z" }, + { url = "https://files.pythonhosted.org/packages/93/5b/6edcd23319d9e28b1bedf32768c3d1fd56eed8223960a2c47dacd2cec2af/lz4-4.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d6da84a26b3aa5da13a62e4b89ab36a396e9327de8cd48b436a3467077f8ccd4", size = 207391, upload-time = "2025-11-03T13:01:36.644Z" }, + { url = "https://files.pythonhosted.org/packages/34/36/5f9b772e85b3d5769367a79973b8030afad0d6b724444083bad09becd66f/lz4-4.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61d0ee03e6c616f4a8b69987d03d514e8896c8b1b7cc7598ad029e5c6aedfd43", size = 207146, upload-time = "2025-11-03T13:01:37.928Z" }, + { url = "https://files.pythonhosted.org/packages/04/f4/f66da5647c0d72592081a37c8775feacc3d14d2625bbdaabd6307c274565/lz4-4.4.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:33dd86cea8375d8e5dd001e41f321d0a4b1eb7985f39be1b6a4f466cd480b8a7", size = 1292623, upload-time = "2025-11-03T13:01:39.341Z" }, + { url = "https://files.pythonhosted.org/packages/85/fc/5df0f17467cdda0cad464a9197a447027879197761b55faad7ca29c29a04/lz4-4.4.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:609a69c68e7cfcfa9d894dc06be13f2e00761485b62df4e2472f1b66f7b405fb", size = 1279982, upload-time = "2025-11-03T13:01:40.816Z" }, + { url = "https://files.pythonhosted.org/packages/25/3b/b55cb577aa148ed4e383e9700c36f70b651cd434e1c07568f0a86c9d5fbb/lz4-4.4.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:75419bb1a559af00250b8f1360d508444e80ed4b26d9d40ec5b09fe7875cb989", size = 1368674, upload-time = "2025-11-03T13:01:42.118Z" }, + { url = "https://files.pythonhosted.org/packages/fb/31/e97e8c74c59ea479598e5c55cbe0b1334f03ee74ca97726e872944ed42df/lz4-4.4.5-cp311-cp311-win32.whl", hash = "sha256:12233624f1bc2cebc414f9efb3113a03e89acce3ab6f72035577bc61b270d24d", size = 88168, upload-time = "2025-11-03T13:01:43.282Z" }, + { url = "https://files.pythonhosted.org/packages/18/47/715865a6c7071f417bef9b57c8644f29cb7a55b77742bd5d93a609274e7e/lz4-4.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:8a842ead8ca7c0ee2f396ca5d878c4c40439a527ebad2b996b0444f0074ed004", size = 99491, upload-time = "2025-11-03T13:01:44.167Z" }, + { url = "https://files.pythonhosted.org/packages/14/e7/ac120c2ca8caec5c945e6356ada2aa5cfabd83a01e3170f264a5c42c8231/lz4-4.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:83bc23ef65b6ae44f3287c38cbf82c269e2e96a26e560aa551735883388dcc4b", size = 91271, upload-time = "2025-11-03T13:01:45.016Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ac/016e4f6de37d806f7cc8f13add0a46c9a7cfc41a5ddc2bc831d7954cf1ce/lz4-4.4.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:df5aa4cead2044bab83e0ebae56e0944cc7fcc1505c7787e9e1057d6d549897e", size = 207163, upload-time = "2025-11-03T13:01:45.895Z" }, + { url = "https://files.pythonhosted.org/packages/8d/df/0fadac6e5bd31b6f34a1a8dbd4db6a7606e70715387c27368586455b7fc9/lz4-4.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d0bf51e7745484d2092b3a51ae6eb58c3bd3ce0300cf2b2c14f76c536d5697a", size = 207150, upload-time = "2025-11-03T13:01:47.205Z" }, + { url = "https://files.pythonhosted.org/packages/b7/17/34e36cc49bb16ca73fb57fbd4c5eaa61760c6b64bce91fcb4e0f4a97f852/lz4-4.4.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7b62f94b523c251cf32aa4ab555f14d39bd1a9df385b72443fd76d7c7fb051f5", size = 1292045, upload-time = "2025-11-03T13:01:48.667Z" }, + { url = "https://files.pythonhosted.org/packages/90/1c/b1d8e3741e9fc89ed3b5f7ef5f22586c07ed6bb04e8343c2e98f0fa7ff04/lz4-4.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c3ea562c3af274264444819ae9b14dbbf1ab070aff214a05e97db6896c7597e", size = 1279546, upload-time = "2025-11-03T13:01:50.159Z" }, + { url = "https://files.pythonhosted.org/packages/55/d9/e3867222474f6c1b76e89f3bd914595af69f55bf2c1866e984c548afdc15/lz4-4.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24092635f47538b392c4eaeff14c7270d2c8e806bf4be2a6446a378591c5e69e", size = 1368249, upload-time = "2025-11-03T13:01:51.273Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e7/d667d337367686311c38b580d1ca3d5a23a6617e129f26becd4f5dc458df/lz4-4.4.5-cp312-cp312-win32.whl", hash = "sha256:214e37cfe270948ea7eb777229e211c601a3e0875541c1035ab408fbceaddf50", size = 88189, upload-time = "2025-11-03T13:01:52.605Z" }, + { url = "https://files.pythonhosted.org/packages/a5/0b/a54cd7406995ab097fceb907c7eb13a6ddd49e0b231e448f1a81a50af65c/lz4-4.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:713a777de88a73425cf08eb11f742cd2c98628e79a8673d6a52e3c5f0c116f33", size = 99497, upload-time = "2025-11-03T13:01:53.477Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7e/dc28a952e4bfa32ca16fa2eb026e7a6ce5d1411fcd5986cd08c74ec187b9/lz4-4.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:a88cbb729cc333334ccfb52f070463c21560fca63afcf636a9f160a55fac3301", size = 91279, upload-time = "2025-11-03T13:01:54.419Z" }, +] + +[[package]] +name = "markdown" +version = "3.10.2" +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" } +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" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +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/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.optional-dependencies] +linkify = [ + { name = "linkify-it-py" }, +] +plugins = [ + { name = "mdit-py-plugins" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.9" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "cycler", marker = "python_full_version < '3.11'" }, + { name = "fonttools", marker = "python_full_version < '3.11'" }, + { name = "kiwisolver", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pillow", marker = "python_full_version < '3.11'" }, + { name = "pyparsing", marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/6f/340b04986e67aac6f66c5145ce68bf72c64bed30f92c8913499a6e6b8f99/matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217", size = 8296625, upload-time = "2026-04-24T00:11:43.376Z" }, + { url = "https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b", size = 8188790, upload-time = "2026-04-24T00:11:46.556Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37", size = 8769389, upload-time = "2026-04-24T00:11:48.959Z" }, + { url = "https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294", size = 9589657, upload-time = "2026-04-24T00:11:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/5f/15/5246f7b43beae19c74dfee651d58d6cc8112e06f77adb4e88cc04f2e3a23/matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65", size = 9651983, upload-time = "2026-04-24T00:11:54.766Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/5acecfe672ba0fa1b8c0454f69ce155d1e6fc5852fa7206bf9afaf767121/matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda", size = 8199701, upload-time = "2026-04-24T00:11:58.389Z" }, + { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860, upload-time = "2026-04-24T00:12:01.207Z" }, + { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254, upload-time = "2026-04-24T00:12:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092, upload-time = "2026-04-24T00:12:06.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691, upload-time = "2026-04-24T00:12:09.706Z" }, + { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771, upload-time = "2026-04-24T00:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112, upload-time = "2026-04-24T00:12:15.773Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310, upload-time = "2026-04-24T00:12:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, + { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, + { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, + { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2b/0e92ad0ac446633f928a1563db4aa8add407e1924faf0ded5b95b35afb27/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b", size = 8293058, upload-time = "2026-04-24T00:13:56.339Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/74682fd369f5299ceda438fea2a0662e6383b85c9383fb9cdfcf04713e07/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f", size = 8186627, upload-time = "2026-04-24T00:13:58.623Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e8/368aab88f3c4cd8992800f31abfe0670c3e47540ba20a97e9fdbcde594b3/matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585", size = 8764117, upload-time = "2026-04-24T00:14:01.684Z" }, + { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420, upload-time = "2026-04-24T00:14:04.57Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981, upload-time = "2026-04-24T00:14:07.233Z" }, + { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002, upload-time = "2026-04-24T00:14:09.816Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.11.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler", marker = "python_full_version >= '3.11'" }, + { name = "fonttools", marker = "python_full_version >= '3.11'" }, + { name = "kiwisolver", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pillow", marker = "python_full_version >= '3.11'" }, + { name = "pyparsing", marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/a2/78f662f1b18968531f67d3fcde1b7ea8496920bacd4f16ddb5b79d112e46/matplotlib-3.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f857524b442f0f36e641868ce2171aafa88cb0bc0644f4e1d8a5df9b32649fef", size = 9436261, upload-time = "2026-06-12T02:27:34.161Z" }, + { url = "https://files.pythonhosted.org/packages/5e/92/044f1de43901310202f4c79acf4f141be53b2ca8d8380e2fcefb3d523a75/matplotlib-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57baa92fdc82948ed716eae6d2579d4d6f40965cd8d2f416755b4a72580a3233", size = 9264669, upload-time = "2026-06-12T02:27:37.413Z" }, + { url = "https://files.pythonhosted.org/packages/53/f4/f0b4f9ba7ec14a7af8151f3ad71ecfe3561e6ba38cfab1db3681ba4ca112/matplotlib-3.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630eee0e67d35cce2019a0e670719f4816e3b86aff0fa72729f6c69786fceb45", size = 10021076, upload-time = "2026-06-12T02:27:39.926Z" }, + { url = "https://files.pythonhosted.org/packages/d7/33/4d679c6dcd594a156542080ac907ddccf7b09ca11655c4b28eca8e9ee5da/matplotlib-3.11.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5106c444d0bf966eee2853548c03772af4ab7199118e086c62fbac8ccb07c055", size = 10828999, upload-time = "2026-06-12T02:27:42.433Z" }, + { url = "https://files.pythonhosted.org/packages/07/74/0a3683802037d8cd013144d77c247219b47f2aabace6fdde74faa12bacf7/matplotlib-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d7aea652b58e686444079be3376ef546bffa1eee9b9bb9c472b9fcf6cf410d3", size = 10913103, upload-time = "2026-06-12T02:27:44.827Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9f/970fcbf381e82ec66fdf5da8ea76e2e9240f61a24011ce9fd1d42c37ac2d/matplotlib-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:70a5b3e9a5dab708c0f039709ae7c68d5b4d254e291ef76492cdba230c8bb5e4", size = 9310945, upload-time = "2026-06-12T02:27:46.867Z" }, + { url = "https://files.pythonhosted.org/packages/14/4e/6e7cfed23611265ded53806852343b5c59339e506e84c474a9b5afc3b249/matplotlib-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:3d68266213e73823ac3be90615bab0cf31f88851e114cdb1dd25dacf3b01e1a7", size = 8999304, upload-time = "2026-06-12T02:27:48.798Z" }, + { url = "https://files.pythonhosted.org/packages/da/17/f5276b496c61477a6c4fc5e7401f4bfe1c2e5ef7c6cd67896f2ade3809cb/matplotlib-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b5872e9cf11adc8f589ded3ce11bc3e1061ad498259664fabc1f6615beb918", size = 9449976, upload-time = "2026-06-12T02:27:50.989Z" }, + { url = "https://files.pythonhosted.org/packages/82/34/bdd77418adb2178a1d59f044bd67bfebb115896e91b840b8a197eb3f4f4e/matplotlib-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0515d495124be3124340e59f164d901ed4484e2246a5b74cfa483cac3b80bd97", size = 9279307, upload-time = "2026-06-12T02:27:53.247Z" }, + { url = "https://files.pythonhosted.org/packages/94/95/7f522393c88313336b20d70fc849555757b2e5febc22b83b3a3f0fd4bce9/matplotlib-3.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be5f93a1d21981bfb802ded0d77a0caa92d4342a47d45754fac77e314a506344", size = 10031353, upload-time = "2026-06-12T02:27:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/87/ce/8f25a0e3186aefd61913e7467d1b999465bcd0d0c03ac695c1b26ca559b7/matplotlib-3.11.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41635d7909d19e52e924a521dde6d8f670b0f53ab1d0e8c331fa831554f681d1", size = 10839232, upload-time = "2026-06-12T02:27:57.746Z" }, + { url = "https://files.pythonhosted.org/packages/85/c2/db15da2bbdf9e3ca66df7db8e2c33a1dfed67be24a24d2c878efaaff01d6/matplotlib-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94f5000f67ca9faa300863ea17f8bce9175cb67b88bec4bc7780502d53dd7c9e", size = 10923899, upload-time = "2026-06-12T02:28:00.223Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2f/a58a4443a4d052a4ea77557478336aefc26c7981f6408d37adba763aa758/matplotlib-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac6f1ef39f3d0f9e2463303013094992cdbe0f85f43bc54155bc472b2042768e", size = 9329528, upload-time = "2026-06-12T02:28:02.27Z" }, + { url = "https://files.pythonhosted.org/packages/61/0f/4b669589d47733b97ab9df4b58d6fc1e68acb5ea42a928dc7cbdd6bf5871/matplotlib-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:9dd11fb612ce7bc60b1de5b4fc87ff959d22317b5de42aabf392f66f97af22eb", size = 9003413, upload-time = "2026-06-12T02:28:04.49Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c2/f5da6cd37ed6871f5c9b3c0507ddb69f14d6c36fac4541e4e0c60cb8cdfc/matplotlib-3.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81ae77077a1e16d37a5b61096ccb07c8d90a99b518fa8256b8f21578932f2f62", size = 9434094, upload-time = "2026-06-12T02:29:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/f8/07/56f66906e0f87a0c6d0d0acbd34dbc9432b1931d8f26ef618bd6f92932a9/matplotlib-3.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ddef37840695f5eef65f9f070fe2d2f510f584c2156203f9f622a5b0584efffd", size = 9262183, upload-time = "2026-06-12T02:29:11.283Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d8/c4ecab06b7ea36a570c4f3bd2d48d1799fd5d9174470e45c2194199431e7/matplotlib-3.11.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf662e5ac5707658cb931e19972c4bd99f7b4f8b7bf79d3c821d239fa6b71e64", size = 10015653, upload-time = "2026-06-12T02:29:13.251Z" }, +] + +[[package]] +name = "mcp" +version = "1.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "mujoco" +version = "3.2.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "etils", version = "1.13.0", source = { registry = "https://pypi.org/simple" }, extra = ["epath"], marker = "python_full_version < '3.11'" }, + { name = "etils", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, extra = ["epath"], marker = "python_full_version >= '3.11'" }, + { name = "glfw" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pyopengl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/49/80984c24d1b8724257edf19b9d2c2db0a1fb5aa97f8ab77064666cd61923/mujoco-3.2.7.tar.gz", hash = "sha256:6b4013466b45575d49dd3d8748d09985f7349003aef32687216d2b902ac7b138", size = 727524, upload-time = "2025-01-14T23:44:17.635Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/39/613fe4b59a465a0663b912a8869e685eba3fa5987e4d2ffc1b607c2d8016/mujoco-3.2.7-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:2bd8736dd0b8f869940ba71f646aef094503346bc8ee774cefefa8f71d2de421", size = 6580217, upload-time = "2025-01-14T23:42:20.974Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/3120e1e6a2480c280c24c63044ff51bd7421eb8f23c4e9f1160b1b5ebc86/mujoco-3.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:219585ca6ac9ab61482413fdd02bcb0751984a38d19e0f58b1436c00eb10fdb2", size = 6353600, upload-time = "2025-01-14T23:42:26.291Z" }, + { url = "https://files.pythonhosted.org/packages/54/f8/821fd68985f691b45249a3a3edc58e186d07f8123fffc63f71a2eb499b58/mujoco-3.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e66b5cd4a458d1e6c9a2086d5dbc820d6df05fc4dc927945fc0a888ead3bf98", size = 6121363, upload-time = "2025-01-14T23:42:32.712Z" }, + { url = "https://files.pythonhosted.org/packages/4c/86/dba69d5a7be31a80886a7f70262aa71fde2fc0c5f830ff06570116fa80f0/mujoco-3.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bec3d3dd430421da3d47e81710b104c1146fe5af959015786e50b8bc3bb92ff", size = 6398446, upload-time = "2025-01-14T23:42:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/9f/4a/b48b5374b6adcd4367db6b37ef4c5085b8e275f1e850e9e1eed24761d615/mujoco-3.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:694c1931f1c5f7395b297c81cb7bbc36609d7ca45b4c280f7af5ee1d2398b26d", size = 4686736, upload-time = "2025-01-14T23:42:42.129Z" }, + { url = "https://files.pythonhosted.org/packages/af/e0/c6088e099290a50f3cb71a6226ecf9cfb270f7d69041ad64d91c0782a8bc/mujoco-3.2.7-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:4928eb4c56f3fae55f15dfe8c10f05d16006d6935451921b45be04912580ec1c", size = 6592792, upload-time = "2025-01-14T23:42:46.555Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a8/0dd4a6839b78b2c5f52e967db73cdedf27f48e69bb9041432254c0e3f5f7/mujoco-3.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0b33e79acf8b4f767d3a75cf1ff06b134ff309b7674e31d27aed9ea55ef16ae3", size = 6364740, upload-time = "2025-01-14T23:42:50.772Z" }, + { url = "https://files.pythonhosted.org/packages/2d/10/2f5826f3362b6a015798bea37195a4a9c965ee2c7f5ab87f9da6f1d7cdc7/mujoco-3.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b6bfaf88350d5a2f9b65da9774346582d7a46690644fd5693bec8111aed515a", size = 6135745, upload-time = "2025-01-14T23:42:59.477Z" }, + { url = "https://files.pythonhosted.org/packages/c6/63/03c5bc45b86bccb683e516637bbcf7521bbfb49fb33b725f6e597a4cbee2/mujoco-3.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43c1cba5cb44d36840721166feb74600c193452f8510c0b249c4208cb534a171", size = 6412699, upload-time = "2025-01-14T23:43:04.273Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/c5122d6f362a371c8d96f2bd475a5ad57a4a109f900da1e2852caf15bbde/mujoco-3.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:8b96f3209ef8b6d8f4535a725282574e99e3420ff8fab695bd514a74dec56852", size = 4711303, upload-time = "2025-01-14T23:43:11.506Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b0/bba9ee9ea349141c6f0ea041f5bf6e4372864d66547775f98d67091dc4fc/mujoco-3.2.7-cp312-cp312-macosx_10_16_x86_64.whl", hash = "sha256:53dea52c1274aee8bbba9a4930f51eeb643d6d2ce010dd47d1535fbee9231b39", size = 6600914, upload-time = "2025-01-14T23:43:15.045Z" }, + { url = "https://files.pythonhosted.org/packages/6a/21/acad478f7e11a4ffca68722f3b73e10eba3be0d56911ff22e61f85b195a8/mujoco-3.2.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e1e48423af68ba8c6bd284006bee7a02b1d65392a2ace2cc533447516a2f1d11", size = 6334825, upload-time = "2025-01-14T23:43:19.655Z" }, + { url = "https://files.pythonhosted.org/packages/c1/aa/0a21f086c3a76e4831870ec9665a0d131aa1bf64fd2243067846a98e908d/mujoco-3.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d8c75d0139ff16ee4b4d3f6496cd2016e53efa35fcc61932fa16cb4c9e469e6", size = 6142889, upload-time = "2025-01-14T23:43:25.205Z" }, + { url = "https://files.pythonhosted.org/packages/4e/13/3a9ee67cececbcccd63ab97638e42c16fe70306d72478df040fc04d75193/mujoco-3.2.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:212c46bf118230dc52a44c0c0679058e1b5d74c1777272de966a833879d1896b", size = 6486171, upload-time = "2025-01-14T23:43:29.976Z" }, + { url = "https://files.pythonhosted.org/packages/07/11/874711848a32ce0177934e97be9096951fe9b552194b60d13b2a8fdb32c6/mujoco-3.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:d38d3a677f4c9d6a932c84a350f1c426a32011988614d06ea2725c3cd67b6429", size = 4760189, upload-time = "2025-01-14T23:43:32.964Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "narwhals" +version = "2.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/ac/66ed1fc6e38a0c0f330627ec5c5d597990d6159b6712b82af0ad2c65f06c/narwhals-2.23.0.tar.gz", hash = "sha256:13e7ff5b4bb4a2f77b907c2e4d8a76e273dfc1323a3c997440a2f9fd26aed408", size = 656209, upload-time = "2026-07-01T11:21:53.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/4e/afc8c31605cb8be1d3bb4438c4d979daa104dab6306cd2b87abe9c3a7299/narwhals-2.23.0-py3-none-any.whl", hash = "sha256:769e7b9ab102c93d8fa019f6b4cd1a657909b04a20bf6210e5a35aae06814ae9", size = 458938, upload-time = "2026-07-01T11:21:51.677Z" }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numba" +version = "0.65.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "llvmlite" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/c5/db2ac3685833d626c0dcae6bd2330cd68433e1fd248d15f70998160d3ad7/numba-0.65.1.tar.gz", hash = "sha256:19357146c32fe9ed25059ab915e8465fb13951cf6b0aace3826b76886373ab23", size = 2765600, upload-time = "2026-04-24T02:02:56.551Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1b/3c5a7daf683a95465bf23504bcd1a2d5db8cd5e5e276ca87505d020dffe9/numba-0.65.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:9d993ed0a257aa4116e6f553f114004bcfdee540c7276ab8ea48f650d514c452", size = 2680870, upload-time = "2026-04-24T02:02:10.623Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a4/1831836814018a898e7d252aebe09c0f3ce1f26d145b68264b4ae0be6822/numba-0.65.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f098109f361681e57295f7e84d8ab2426902539a141811de0703ace52826981", size = 3739780, upload-time = "2026-04-24T02:02:13.097Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1b/a813ddc81def09e257d2b1f67521982ce4b06204a87268796ffc8187271c/numba-0.65.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973fd8173f2312815e6b7aaae887c4ce8a817eeff46a4f8840b828305b75bc95", size = 3446722, upload-time = "2026-04-24T02:02:15.083Z" }, + { url = "https://files.pythonhosted.org/packages/09/52/ee1d8b3becda384fe0552221641e05aa668a35e8a77470db4db7f6475000/numba-0.65.1-cp310-cp310-win_amd64.whl", hash = "sha256:c63aa0c4193694026452da55d0ef9d85156c1a7a333454c103bb30dec81b7bf8", size = 2747539, upload-time = "2026-04-24T02:02:16.79Z" }, + { url = "https://files.pythonhosted.org/packages/96/b3/650500c2eab4534d98e9166f4298e0f3c69c742afdf24e6eabccd1f16ad8/numba-0.65.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:7020d74b19cdb8cff16506542fdd510756e28c5e7f3bd0b7f574f0f42272fcd9", size = 2680563, upload-time = "2026-04-24T02:02:18.414Z" }, + { url = "https://files.pythonhosted.org/packages/44/0b/0615dbedb98f5b32a35a53290fbdc6e22306968109278d7e58df82d7a9f6/numba-0.65.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f80ed83774b5173abd6581cd8d2165d1d38e13d2e5c8155c0c0b421784745420", size = 3745018, upload-time = "2026-04-24T02:02:20.252Z" }, + { url = "https://files.pythonhosted.org/packages/49/aa/4361698f35bf63bff67dfe6c90493731177f48ede954f77b0588731537bc/numba-0.65.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ed425a43b0a5f9772f2f4e2dd0bbd12eabecae1af0b24efcfd4e053f012aac6", size = 3450962, upload-time = "2026-04-24T02:02:22.449Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9a/af61ec03b3116c161fd7a06b9e8a265729a8718458333e8ffbb06d9a3978/numba-0.65.1-cp311-cp311-win_amd64.whl", hash = "sha256:df40a5028a975b9ea66f6a2a3f7abbdbd541a863070e34ed367aff21141248e4", size = 2747417, upload-time = "2026-04-24T02:02:24.43Z" }, + { url = "https://files.pythonhosted.org/packages/57/bc/76f8f8c5cf9adee47fdb7bbb03be8900f76f902d451d7477cf12b845e1de/numba-0.65.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ac3f1e77c352dd0ea9712732c2d8f9ca507717435eec5b5013bf138ac33c4a08", size = 2681371, upload-time = "2026-04-24T02:02:26.105Z" }, + { url = "https://files.pythonhosted.org/packages/69/47/a415af0283e4db0398104c6d1c11c9861a98dc67a7aa442a7769ed5d6196/numba-0.65.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:52bc6f3ceb8fcaff9b2ae26b4c6b1e9fee39db8d355534c0fe4f39a901246b84", size = 3802467, upload-time = "2026-04-24T02:02:27.712Z" }, + { url = "https://files.pythonhosted.org/packages/46/36/246f73ec99cfeab2f2cb2ce7d4218766cc36a2da418901223f4f4da9c813/numba-0.65.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90ca10b3463bae0bd70589726fe3c77d01d6b5fc86bee54bcdf9fb6b47c28977", size = 3502628, upload-time = "2026-04-24T02:02:29.763Z" }, + { url = "https://files.pythonhosted.org/packages/db/9e/3c679b2ee078425b9e99a91e44f8d132a6830d8ccce5227bc5e9181aeed8/numba-0.65.1-cp312-cp312-win_amd64.whl", hash = "sha256:5971c632be2a2351500431f46213821dba8d02b18a9f7d02fd36bd2743e41a6a", size = 2750611, upload-time = "2026-04-24T02:02:31.477Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "nvidia-cublas" +version = "13.1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cuda-nvrtc", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.20.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cusparse", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" }, + { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.29.7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" }, + { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, +] + +[[package]] +name = "omegaconf" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/3d/e4b57b8d9008c6ebe0d5eff901f91d5700cf7bdb8c8863df817463a7fd5e/omegaconf-2.3.1.tar.gz", hash = "sha256:e5e7de64aeebeddaf8e6d3f7a783b32ac2a01c0fbd9c878012caecb891a1f42a", size = 3298472, upload-time = "2026-06-11T05:05:12.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/0e/152509871bf30df6fc38569f52a2db9b55dd41aae957adae50a053ac7778/omegaconf-2.3.1-py3-none-any.whl", hash = "sha256:3d701d14e9a8828f1edd28bb70b725908b34277cdd72cf7d6a83f94dadc6b6a0", size = 79502, upload-time = "2026-06-11T05:05:09.954Z" }, +] + +[[package]] +name = "open3d" +version = "0.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "addict" }, + { name = "configargparse" }, + { name = "dash" }, + { name = "flask" }, + { name = "matplotlib", version = "3.10.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "matplotlib", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nbformat" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, + { name = "pyquaternion" }, + { name = "pyyaml" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tqdm" }, + { name = "werkzeug" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/4b/91e8a4100adf0ccd2f7ad21dd24c2e3d8f12925396528d0462cfb1735e5a/open3d-0.19.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:f7128ded206e07987cc29d0917195fb64033dea31e0d60dead3629b33d3c175f", size = 103086005, upload-time = "2025-01-08T07:25:56.755Z" }, + { url = "https://files.pythonhosted.org/packages/c7/45/13bc9414ee9db611cba90b9efa69f66f246560e8ade575f1ee5b7f7b5d31/open3d-0.19.0-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:5b60234fa6a56a20caf1560cad4e914133c8c198d74d7b839631c90e8592762e", size = 447678387, upload-time = "2025-01-08T07:21:55.27Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1c/0219416429f88ebc94fcb269fb186b153affe5b91dffe8f9062330d7776d/open3d-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:18bb8b86e5fa9e582ed11b9651ff6e4a782e6778c9b8bfc344fc866dc8b5f49c", size = 69150378, upload-time = "2025-01-08T07:27:10.462Z" }, + { url = "https://files.pythonhosted.org/packages/a7/37/8d1746fcb58c37a9bd868fdca9a36c25b3c277bd764b7146419d11d2a58d/open3d-0.19.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:117702467bfb1602e9ae0ee5e2c7bcf573ebcd227b36a26f9f08425b52c89929", size = 103098641, upload-time = "2025-01-08T07:26:12.371Z" }, + { url = "https://files.pythonhosted.org/packages/bc/50/339bae21d0078cc3d3735e8eaf493a353a17dcc95d76bcefaa8edcf723d3/open3d-0.19.0-cp311-cp311-manylinux_2_31_x86_64.whl", hash = "sha256:678017392f6cc64a19d83afeb5329ffe8196893de2432f4c258eaaa819421bb5", size = 447683616, upload-time = "2025-01-08T07:22:48.098Z" }, + { url = "https://files.pythonhosted.org/packages/a3/3c/358f1cc5b034dc6a785408b7aa7643e503229d890bcbc830cda9fce778b1/open3d-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:02091c309708f09da1167d2ea475e05d19f5e81dff025145f3afd9373cbba61f", size = 69151111, upload-time = "2025-01-08T07:27:22.662Z" }, + { url = "https://files.pythonhosted.org/packages/37/c5/286c605e087e72ad83eab130451ce13b768caa4374d926dc735edc20da5a/open3d-0.19.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:9e4a8d29443ba4c83010d199d56c96bf553dd970d3351692ab271759cbe2d7ac", size = 103202754, upload-time = "2025-01-08T07:26:27.169Z" }, + { url = "https://files.pythonhosted.org/packages/2b/95/3723e5ade77c234a1650db11cbe59fe25c4f5af6c224f8ea22ff088bb36a/open3d-0.19.0-cp312-cp312-manylinux_2_31_x86_64.whl", hash = "sha256:01e4590dc2209040292ebe509542fbf2bf869ea60bcd9be7a3fe77b65bad3192", size = 447665185, upload-time = "2025-01-08T07:23:39.769Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c4/35a6e0a35aa72420e75dc28d54b24beaff79bcad150423e47c67d2ad8773/open3d-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:665839837e1d3a62524804c31031462c3b548a2b6ed55214e6deb91522844f97", size = 69169961, upload-time = "2025-01-08T07:27:35.392Z" }, +] + +[[package]] +name = "open3d-unofficial-arm" +version = "0.19.0.post9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "configargparse", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "dash", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "flask", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nbformat", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "werkzeug", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/f9/edcfaa213800ea278804402baa65693840bc7a323b3de8a31c54ce4e42c8/open3d_unofficial_arm-0.19.0.post9.tar.gz", hash = "sha256:ee300bd557f04750db6e47ccb6c6867c6dd6cfc04169dddeb92505da9ea739ef", size = 5327, upload-time = "2026-04-16T21:21:11.152Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/e1/847917ccac62d6d1f680f69feb333c513cb1bf7d7ff14c9d1618a5487bec/open3d_unofficial_arm-0.19.0.post9-cp310-cp310-manylinux_2_31_aarch64.whl", hash = "sha256:f8e6923dcfe3c0a0e3ddaf30babc463aa1e71464750be100a2b382eb61d4fc6a", size = 47332813, upload-time = "2026-04-16T21:23:29.257Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4f/c886749c286cdc1163da559c8a7447c11e6ee32cf1b9bbf3a36a3a865739/open3d_unofficial_arm-0.19.0.post9-cp310-cp310-manylinux_2_35_aarch64.whl", hash = "sha256:97a5eaf992816610b2c7d474ecc3ebe3c5044441611883ccfb1201d69030e0a4", size = 48230532, upload-time = "2026-04-16T21:23:43.001Z" }, + { url = "https://files.pythonhosted.org/packages/f0/63/657916febb68b6d65539ea57bc50c9e82622a1bbb5af527950d7b3f49644/open3d_unofficial_arm-0.19.0.post9-cp311-cp311-manylinux_2_35_aarch64.whl", hash = "sha256:3b407104a000f0e44b27730e84ffef85e25ce6fb9bb09e5368c462449eb4e6f0", size = 48233468, upload-time = "2026-04-16T21:23:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/a9/9c/863a42c39d2f3d8dbed617e624b3bac04225a56be193b999009954ec0cac/open3d_unofficial_arm-0.19.0.post9-cp312-cp312-manylinux_2_31_aarch64.whl", hash = "sha256:e104c90aa35ee7c4e2470abb225522f39cff5ab2f3cf9297680be9367e846e56", size = 47305232, upload-time = "2026-04-16T21:24:09.956Z" }, + { url = "https://files.pythonhosted.org/packages/72/01/4f2ee6cadddf2bd87dcb6f0f43fdf5acca6b24c550c695c20e328567e61a/open3d_unofficial_arm-0.19.0.post9-cp312-cp312-manylinux_2_35_aarch64.whl", hash = "sha256:4f7c325cad5c589363723967b1275a2b6fc3776ebaee4476a44f4baef5754c24", size = 48221802, upload-time = "2026-04-16T21:24:24.015Z" }, +] + +[[package]] +name = "opencv-python" +version = "4.13.0.92" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/6f/5a28fef4c4a382be06afe3938c64cc168223016fa520c5abaf37e8862aa5/opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19", size = 46247052, upload-time = "2026-02-05T07:01:25.046Z" }, + { url = "https://files.pythonhosted.org/packages/08/ac/6c98c44c650b8114a0fb901691351cfb3956d502e8e9b5cd27f4ee7fbf2f/opencv_python-4.13.0.92-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:5868a8c028a0b37561579bfb8ac1875babdc69546d236249fff296a8c010ccf9", size = 32568781, upload-time = "2026-02-05T07:01:41.379Z" }, + { url = "https://files.pythonhosted.org/packages/3e/51/82fed528b45173bf629fa44effb76dff8bc9f4eeaee759038362dfa60237/opencv_python-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bc2596e68f972ca452d80f444bc404e08807d021fbba40df26b61b18e01838a", size = 47685527, upload-time = "2026-02-05T06:59:11.24Z" }, + { url = "https://files.pythonhosted.org/packages/db/07/90b34a8e2cf9c50fe8ed25cac9011cde0676b4d9d9c973751ac7616223a2/opencv_python-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:402033cddf9d294693094de5ef532339f14ce821da3ad7df7c9f6e8316da32cf", size = 70460872, upload-time = "2026-02-05T06:59:19.162Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/7a9cc719b3eaf4377b9c2e3edeb7ed3a81de41f96421510c0a169ca3cfd4/opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bccaabf9eb7f897ca61880ce2869dcd9b25b72129c28478e7f2a5e8dee945616", size = 46708208, upload-time = "2026-02-05T06:59:15.419Z" }, + { url = "https://files.pythonhosted.org/packages/fd/55/b3b49a1b97aabcfbbd6c7326df9cb0b6fa0c0aefa8e89d500939e04aa229/opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:620d602b8f7d8b8dab5f4b99c6eb353e78d3fb8b0f53db1bd258bb1aa001c1d5", size = 72927042, upload-time = "2026-02-05T06:59:23.389Z" }, + { url = "https://files.pythonhosted.org/packages/fb/17/de5458312bcb07ddf434d7bfcb24bb52c59635ad58c6e7c751b48949b009/opencv_python-4.13.0.92-cp37-abi3-win32.whl", hash = "sha256:372fe164a3148ac1ca51e5f3ad0541a4a276452273f503441d718fab9c5e5f59", size = 30932638, upload-time = "2026-02-05T07:02:14.98Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a5/1be1516390333ff9be3a9cb648c9f33df79d5096e5884b5df71a588af463/opencv_python-4.13.0.92-cp37-abi3-win_amd64.whl", hash = "sha256:423d934c9fafb91aad38edf26efb46da91ffbc05f3f59c4b0c72e699720706f5", size = 40212062, upload-time = "2026-02-05T07:02:12.724Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" }, + { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, +] + +[[package]] +name = "pillow" +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/25/c2/669d88644cddb1485bd9534e63e8cf476c8e51cb3c3a1297677023505c0e/pillow-12.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a", size = 5392418, upload-time = "2026-07-01T11:53:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ba/3762f376a2948e3036488d773a146e0ae6ecc2ca03ac20e2615bd0b2ba02/pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7", size = 4785287, upload-time = "2026-07-01T11:53:29.761Z" }, + { url = "https://files.pythonhosted.org/packages/07/50/b5d688cc9c52d4482f3d5bcab6ce20bc2a74a85d2343841c907444a3be2c/pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f", size = 6253754, upload-time = "2026-07-01T11:53:32.298Z" }, + { url = "https://files.pythonhosted.org/packages/4e/89/36f4cd76cf4baf05c50ababb976249153f18c959171c7f6ba09a6f217260/pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec", size = 6925605, upload-time = "2026-07-01T11:53:34.487Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c0/4de58cf6633b9e3a6061ef4be6fb91fc3c90b812ece886f531e3c523d777/pillow-12.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468", size = 6327788, upload-time = "2026-07-01T11:53:36.433Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/14d53682a19550dbbaf3b598f807d5457646c510805a44c7d7891cd1cd1a/pillow-12.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed", size = 7036288, upload-time = "2026-07-01T11:53:38.712Z" }, + { url = "https://files.pythonhosted.org/packages/38/1d/36279e3c77efe034e4cc2b0393ee74ffdb5a62391dacbf9b916154f5f0b8/pillow-12.3.0-cp310-cp310-win32.whl", hash = "sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1", size = 6472396, upload-time = "2026-07-01T11:53:40.781Z" }, + { url = "https://files.pythonhosted.org/packages/48/7c/8fa0039574c476d7c6fa57dd7c32a130436877c6ec1e5ce1cc8ec44878c1/pillow-12.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb", size = 7226887, upload-time = "2026-07-01T11:53:42.764Z" }, + { url = "https://files.pythonhosted.org/packages/fa/17/e324be141d173c1c919428066c3259f21c1b8982e564e01a4a81e96dbdcf/pillow-12.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f", size = 2568039, upload-time = "2026-07-01T11:53:45.372Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c8/0a78b0e02d7ac54bc03e5321c9220da52f0c2ea83b21f7c40e7f3169c502/pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756", size = 5392415, upload-time = "2026-07-01T11:53:47.162Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5b/a02d30018abd97ced9f5a6c63d28597694a00d066516b9c1c6de45859fc9/pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6", size = 4785266, upload-time = "2026-07-01T11:53:49.079Z" }, + { url = "https://files.pythonhosted.org/packages/c8/98/766667a4be768150a202836acd9fad19c06824ca86c4286d3cf6b274964e/pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd", size = 6263814, upload-time = "2026-07-01T11:53:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2d/ede717bc1144f63886c21fd349bb95860b0d1a21149ff16f2bb362b612b6/pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd", size = 6934408, upload-time = "2026-07-01T11:53:53.487Z" }, + { url = "https://files.pythonhosted.org/packages/a3/48/9c58b685e69d49c31af6c8eb9012055fab7e665785165c84796e2c73ce72/pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c", size = 6337160, upload-time = "2026-07-01T11:53:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/ff/fa/dc2a5c0ba6df93f67c31d34b808b7ce440b40cdbf96f0b81cde1d1e6fa93/pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5", size = 7045172, upload-time = "2026-07-01T11:53:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/86/a5/444817a4d4c4c2417df00513086ca196f388d8f9ef40c2e4ccd1ad1af54b/pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b", size = 6472232, upload-time = "2026-07-01T11:53:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/63/c6/4bad1b18d132a50b27e1365e1ab163616f7a5bb56d330f66f9d1d9d4f9d4/pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a", size = 7233653, upload-time = "2026-07-01T11:54:02.066Z" }, + { url = "https://files.pythonhosted.org/packages/fd/16/00f91ab7760dc842f5aad55217e80fc4a7067a0604535249bc8a2d6d9870/pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26", size = 2568195, upload-time = "2026-07-01T11:54:04.622Z" }, + { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, + { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, + { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, + { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, + { url = "https://files.pythonhosted.org/packages/75/18/2e8b40223153ccbc60df07f9e8928dc0c76202aa4e55ae9f53962b6510d6/pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468", size = 5302510, upload-time = "2026-07-01T11:56:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/46/3e/51fabf59d5ab801ceab709453d3ab6b180083496579549de4c45ced6528a/pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94", size = 4736058, upload-time = "2026-07-01T11:56:28.041Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/22fe9384b7949e25fb1293bcfc84fb82590ff4ea6b37c95b24d26d793d86/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e", size = 5237776, upload-time = "2026-07-01T11:56:30.263Z" }, + { url = "https://files.pythonhosted.org/packages/08/14/f6ba68107680ffa74b39985f3f30884e41318fbc4250caa423c79b4788bb/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3", size = 5860358, upload-time = "2026-07-01T11:56:32.68Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0169bc772ec491108b62f644f8ecf1fe5d8ae5ebafde2ee2142210166903/pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a", size = 7231786, upload-time = "2026-07-01T11:56:35.046Z" }, +] + +[[package]] +name = "pin" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cmeel" }, + { name = "cmeel-boost" }, + { name = "cmeel-urdfdom" }, + { name = "coal" }, + { name = "libpinocchio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/40/72fa61797578c9121824645398497d2561ec29eb1d9cb88ca43973435834/pin-4.0.0.tar.gz", hash = "sha256:5bdf7dbf76437f797e8ea4f9a0e286f33e2af3fde38d67efc34832da2edced66", size = 4417957, upload-time = "2026-05-21T17:27:18.033Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/f4/9ee84da0310e4c39ecb3bbb1512d47f065cdbcbfd78f5fc598ef54f69392/pin-4.0.0-0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:354867681a0fcf431b9c1eac4a69fda7db93ee584cb5ef3c719dbb28831b6b49", size = 7397250, upload-time = "2026-05-21T17:26:36.499Z" }, + { url = "https://files.pythonhosted.org/packages/92/eb/ab48a506084c04af7b50ba432bb436e79b21cd142821726002375ba76b75/pin-4.0.0-0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e97bc2b58b9f334cdf082913f762c88b50582ca65156086e26f693f31d57f59b", size = 7134471, upload-time = "2026-05-21T17:26:38.895Z" }, + { url = "https://files.pythonhosted.org/packages/84/1f/fee9f824f7b553e892105098d1ad52a01b7e6d44968f99b022b3147d32ca/pin-4.0.0-0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:70d8d911aeb7cf2f08605358707728d93f17e766e828ae549e2814bc677bdbe1", size = 9981628, upload-time = "2026-05-21T17:26:40.596Z" }, + { url = "https://files.pythonhosted.org/packages/2f/91/aa9230695ba54caa37107a9087e6a3432708eef1387214e3fe1c257dc8b2/pin-4.0.0-0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3f9b98a43610478f6e2f555b60cf3040bf9ece25986416347a503e38b76ce04d", size = 10208985, upload-time = "2026-05-21T17:26:42.547Z" }, + { url = "https://files.pythonhosted.org/packages/05/59/551829f6dd81dcb84ea681f107085afae99a45db954083aac1cbaff8a8fa/pin-4.0.0-0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c123b3627af8fbfff255954bb115517cc5a8f3c572765ec259cf67b8eb6d2c7", size = 7397256, upload-time = "2026-05-21T17:26:44.54Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a5/d91b71ddaa46e0a3990514aa9488631acf8527a26a76f6328cd662dabdeb/pin-4.0.0-0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:322b6c15b8992e01b7ea5f5de91e39dec54022b2c862dd9a0766ac446d7dfc47", size = 7134478, upload-time = "2026-05-21T17:26:46.59Z" }, + { url = "https://files.pythonhosted.org/packages/1b/0e/ed1a5716caf890c4b02481552d60f12df4a587716af453d7d4f8ad9b7198/pin-4.0.0-0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4a81cc911ca16911fffcedcacc9c159f1961691cb1fa65cadef6559515aea136", size = 9979416, upload-time = "2026-05-21T17:26:48.822Z" }, + { url = "https://files.pythonhosted.org/packages/0d/75/cc1bc40bace8ee7382f417f4dacceabcdcd20b716fa66a5de029f98a4715/pin-4.0.0-0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:62ce8dfcc92f43c0f765d9e145a21da914f9b2adb9ee724e87a870f03995fe2d", size = 10207826, upload-time = "2026-05-21T17:26:51.227Z" }, + { url = "https://files.pythonhosted.org/packages/91/ab/f2f7e8b5eba232adba310dafbf78c1a8722fde77a5c5defab88693bfe90e/pin-4.0.0-0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8be5f63b34b7f5da40e6f1f4554ff2f0606c425f89347b0f25f5ce0d3d42a56b", size = 7469991, upload-time = "2026-05-21T17:26:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/88/ca/8cd3f29177213abf9c9714039e1e916694edc935b6644fc33bb037b358d3/pin-4.0.0-0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1abe8107a14d7507983324e3866ed043653d846029148299d1965d0ce32d31e7", size = 7185247, upload-time = "2026-05-21T17:26:55.148Z" }, + { url = "https://files.pythonhosted.org/packages/e0/dd/76c375d6d0e044094686c566f2e14d39a86f35e1286e546be6d05a82cf6e/pin-4.0.0-0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1d9b4df62e71e1be729ceadc344aeda65dac3581caa4368f2e4377a991b66329", size = 9880446, upload-time = "2026-05-21T17:26:57.183Z" }, + { url = "https://files.pythonhosted.org/packages/c0/60/5d3400107ea829ff9002786ea483d0ac6590caaa8ba427c2d5f588a1a3cd/pin-4.0.0-0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8ec1a71de6d8ed6de9e567c5c3dff22a044a3c3671590ee887d930763b267ee3", size = 10117715, upload-time = "2026-05-21T17:26:59.353Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, +] + +[[package]] +name = "plotext" +version = "5.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/d7/f75f397af966fe252d0d34ffd3cae765317fce2134f925f95e7d6725d1ce/plotext-5.3.2.tar.gz", hash = "sha256:52d1e932e67c177bf357a3f0fe6ce14d1a96f7f7d5679d7b455b929df517068e", size = 61967, upload-time = "2024-09-24T15:13:37.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/1e/12fe7c40cd2099a1f454518754ed229b01beaf3bbb343127f0cc13ce6c22/plotext-5.3.2-py3-none-any.whl", hash = "sha256:394362349c1ddbf319548cfac17ca65e6d5dfc03200c40dfdc0503b3e95a2283", size = 64047, upload-time = "2024-09-24T15:13:36.296Z" }, +] + +[[package]] +name = "plotly" +version = "6.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "narwhals" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/fd/d72c292d78aadb93d1a9bcd76bf3c678271040c7cf10abe5788b33040a39/plotly-6.8.0.tar.gz", hash = "sha256:e088e7ddc68d4f70e3d66659224727a45296d71d2b8284181862d3d8f1f0d88f", size = 6915161, upload-time = "2026-06-03T18:33:40.226Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/14/abe5ce876ab5b66ee3c691bf537fcd43d037aea55d447aacf74630a8f31e/plotly-6.8.0-py3-none-any.whl", hash = "sha256:13c5c4a0f70b74cab1913eda0de49b826df5931708eb6f9c3010040614700ec8", size = 9902055, upload-time = "2026-06-03T18:33:34.26Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "plum-dispatch" +version = "2.5.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/46/ab3928e864b0a88a8ae6987b3da3b7ae32fe0a610264f33272139275dab5/plum_dispatch-2.5.7.tar.gz", hash = "sha256:a7908ad5563b93f387e3817eb0412ad40cfbad04bc61d869cf7a76cd58a3895d", size = 35452, upload-time = "2025-01-17T20:07:31.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/31/21609a9be48e877bc33b089a7f495c853215def5aeb9564a31c210d9d769/plum_dispatch-2.5.7-py3-none-any.whl", hash = "sha256:06471782eea0b3798c1e79dca2af2165bafcfa5eb595540b514ddd81053b1ede", size = 42612, upload-time = "2025-01-17T20:07:26.461Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/56/030b7b4719d53085722893e0009dffb9236aa10bca1b12121bdc5626ef16/propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b", size = 93417, upload-time = "2026-05-08T20:59:15.597Z" }, + { url = "https://files.pythonhosted.org/packages/1a/55/1140a8e067b8ec093a18a4ae7bb0045d9db65da38a08618ddc5e2f1994aa/propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c", size = 53847, upload-time = "2026-05-08T20:59:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/20/42/0e7443c90310498561addf346e7d57fe3c6ba1914e1ba938b5464c7bbfd2/propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb", size = 53512, upload-time = "2026-05-08T20:59:18.64Z" }, + { url = "https://files.pythonhosted.org/packages/b7/db/cf51a71bab2009517d1a7f0ee07657e3bd446c4d69f67e6966cf17bcf956/propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e", size = 58068, upload-time = "2026-05-08T20:59:20.683Z" }, + { url = "https://files.pythonhosted.org/packages/b7/43/39b6bdee9699fa1e1641c519feeb64a67e2a9f93bb465c70776b37a7333f/propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e", size = 61020, upload-time = "2026-05-08T20:59:22.112Z" }, + { url = "https://files.pythonhosted.org/packages/26/0b/843726fbb0a29a8c5684fdb25971823638399f31e52e9d1f06a02dc9aa6b/propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b", size = 62732, upload-time = "2026-05-08T20:59:23.805Z" }, + { url = "https://files.pythonhosted.org/packages/39/6e/899fed76dc1942b8a64193a4f059d7f1a2c7ef65085e8a9366ed8ec0d199/propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d", size = 60140, upload-time = "2026-05-08T20:59:25.389Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/3da4be9b5b879219ad234aa535b3dd4a080ed1ad48d3a73ca07a9e798f22/propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d", size = 60400, upload-time = "2026-05-08T20:59:27.238Z" }, + { url = "https://files.pythonhosted.org/packages/60/2f/09b72b874a9aa0044faf52a69807a6ed618e267ceaa9ec4a63195fa5b504/propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0", size = 58155, upload-time = "2026-05-08T20:59:28.48Z" }, + { url = "https://files.pythonhosted.org/packages/8a/37/97489848c54c95578045473954f10956d619ce6a09e7ac137b71cdcb698b/propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b", size = 57037, upload-time = "2026-05-08T20:59:30.146Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/6c695285ccfc49012743ee9c98212b8c5dd0aed7b63cfd816d4a0f7a1601/propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf", size = 61103, upload-time = "2026-05-08T20:59:31.626Z" }, + { url = "https://files.pythonhosted.org/packages/98/a9/1e500401ca593b0bdb6bf75a70bc2d723835fd53360edff6af70692c7546/propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf", size = 60394, upload-time = "2026-05-08T20:59:32.829Z" }, + { url = "https://files.pythonhosted.org/packages/1f/87/f638b6e375eae0f30a1a2325d8b34fd85fdc785bb9960cf805f3bf1ec69a/propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e", size = 63084, upload-time = "2026-05-08T20:59:35.964Z" }, + { url = "https://files.pythonhosted.org/packages/f6/18/884573f5d97b6d9eba68de759a82c901b7e39d7904d30f7b8d58d42d2a12/propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274", size = 60999, upload-time = "2026-05-08T20:59:38.481Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1a/c3915eb059ceec9e758a56e4cfd955292bc0f201be2176a46b76d94b303a/propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe", size = 39036, upload-time = "2026-05-08T20:59:40.323Z" }, + { url = "https://files.pythonhosted.org/packages/5b/02/1dfd5607501a602d19c1c449d2d193b7d1c611f9246b4059026a1189a80e/propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d", size = 42190, upload-time = "2026-05-08T20:59:42.232Z" }, + { url = "https://files.pythonhosted.org/packages/57/93/f71588ad08b3e6f4b555b5ef215808a3c02b042d0151ad82fa6f15be677a/propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5", size = 38545, upload-time = "2026-05-08T20:59:44.087Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744, upload-time = "2026-05-08T20:59:45.799Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033, upload-time = "2026-05-08T20:59:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754, upload-time = "2026-05-08T20:59:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573, upload-time = "2026-05-08T20:59:50.778Z" }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645, upload-time = "2026-05-08T20:59:52.227Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563, upload-time = "2026-05-08T20:59:53.866Z" }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888, upload-time = "2026-05-08T20:59:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253, upload-time = "2026-05-08T20:59:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558, upload-time = "2026-05-08T20:59:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007, upload-time = "2026-05-08T20:59:59.837Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355, upload-time = "2026-05-08T21:00:01.144Z" }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057, upload-time = "2026-05-08T21:00:02.401Z" }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938, upload-time = "2026-05-08T21:00:03.638Z" }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731, upload-time = "2026-05-08T21:00:04.881Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966, upload-time = "2026-05-08T21:00:06.511Z" }, + { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135, upload-time = "2026-05-08T21:00:08.088Z" }, + { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381, upload-time = "2026-05-08T21:00:09.692Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.6" +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" } +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" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +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/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]] +name = "pyarrow" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/bf/a34fee1d624152124fa8355c42f34195ad5fe5233ce5bb87946432047d52/pyarrow-24.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb", size = 35076681, upload-time = "2026-04-21T08:51:46.845Z" }, + { url = "https://files.pythonhosted.org/packages/1d/41/64180033d7027afce12dc96d0fe1f504c6fa112190582b458acea2399530/pyarrow-24.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147", size = 36684260, upload-time = "2026-04-21T08:51:53.642Z" }, + { url = "https://files.pythonhosted.org/packages/57/02/9b9320e673dd8a99411fac78690f3df92f6dd6f59754c750110bca66d64e/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c", size = 45698566, upload-time = "2026-04-21T10:46:02.133Z" }, + { url = "https://files.pythonhosted.org/packages/67/33/f75e91b9a64c3f33c787e263c93b871ad91b8a4a68c1d5cebddd9840e835/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041", size = 48835562, upload-time = "2026-04-21T10:46:10.278Z" }, + { url = "https://files.pythonhosted.org/packages/a5/63/097510448e47e4091faa41c43ba92f97cecaab8f4535b56a3d149578f634/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491", size = 49394997, upload-time = "2026-04-21T10:46:18.08Z" }, + { url = "https://files.pythonhosted.org/packages/60/6b/c047d6222ab279024a062742d1807e2fbaf27bba88a98637299ff47b9236/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1", size = 51911424, upload-time = "2026-04-21T10:46:25.347Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ba/464cc70761c2a525d97ebd84e21c31ebd47f3ef4bdcee117009f51c46f24/pyarrow-24.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591", size = 27251730, upload-time = "2026-04-21T10:46:30.913Z" }, + { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898, upload-time = "2026-04-21T10:46:36.599Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915, upload-time = "2026-04-21T10:46:42.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931, upload-time = "2026-04-21T10:46:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449, upload-time = "2026-04-21T10:46:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949, upload-time = "2026-04-21T10:47:01.869Z" }, + { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986, upload-time = "2026-04-21T10:47:09.872Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371, upload-time = "2026-04-21T10:47:15.943Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +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/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.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +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/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715, upload-time = "2026-06-19T13:44:55.02Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pyobjc-core" +version = "12.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b4/b1/729f7458a63758bd21716648a8abcd9a0c8f2d2e9897763c8a1a1c7fd31b/pyobjc_core-12.2.1.tar.gz", hash = "sha256:7a7b9b018402342cf32bf1956366896350fbe5c0478cb3ef59778f77abed7f07", size = 1063383, upload-time = "2026-06-19T16:19:39.357Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/62/80fe6b6bea9e9e7abe2e7a91bfd22115219b2263e435d2da09cf480b6f49/pyobjc_core-12.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa5c889961d79d7704f17eeb27f80ee791335819c1bb14babeff7b9ea665b5f0", size = 6486390, upload-time = "2026-06-19T13:29:49.457Z" }, + { url = "https://files.pythonhosted.org/packages/92/87/16564ef5e4568ee0edd9e712d8111dc8b67621d6bb6ff430646ee2d637dd/pyobjc_core-12.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:24b76a63caf0b5369d4a377c7c0438cd70df81539057af3db839bfaa3579e04a", size = 6484662, upload-time = "2026-06-19T16:04:44.979Z" }, + { url = "https://files.pythonhosted.org/packages/8c/88/300ad283bed0c971c52dcac6f70113e138169d4ce6d856ddd03d16081e51/pyobjc_core-12.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a64232bb27ed101d4adc7d42b0e64a6d3331aac7bee7861c037a6777a163f10b", size = 6433347, upload-time = "2026-06-19T16:04:49.341Z" }, +] + +[[package]] +name = "pyobjc-framework-cocoa" +version = "12.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/34/fbe38a204643aa4e1b91391cdce07a34da565a69171ebcad08de7438a556/pyobjc_framework_cocoa-12.2.1.tar.gz", hash = "sha256:b94b37fe5730e5ae1fb0052912cd174e6ec329b0bfba4a012ae5db1014b5864b", size = 3125751, upload-time = "2026-06-19T16:20:05.159Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/c5/f6a5458cc5a598baa7a79ffe86248560c631f5a86f4a3c678fb6a815e78b/pyobjc_framework_cocoa-12.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05443c1494532779cccff83e9792fdd6f0a4800cac56620132bf28e0bf966e9d", size = 387303, upload-time = "2026-06-19T16:07:35.831Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d6/dc66ea8519a0475efbccf73f82cc28066339bb300a27f5e1bf91ab1d7002/pyobjc_framework_cocoa-12.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dc6da84f4fc62cc25463bbb85e77a57b8d5ac6caf9a60702daf2edb601332f15", size = 387298, upload-time = "2026-06-19T16:07:37.412Z" }, + { url = "https://files.pythonhosted.org/packages/f7/cf/1b3b32b2f28f66cc053c3438ef4e6df36a1591945bf05e7399da18d74553/pyobjc_framework_cocoa-12.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:28b9b8bab1c36efb94744786918752d0c1842f5fbb67e7d5ca97b5f736512080", size = 388113, upload-time = "2026-06-19T16:07:38.9Z" }, +] + +[[package]] +name = "pyobjc-framework-corebluetooth" +version = "12.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "pyobjc-framework-cocoa", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d4/91/c76f3c5e8e80c7047e43c4c05b3e6fda9a7cefad5aae85487007674c966c/pyobjc_framework_corebluetooth-12.2.1.tar.gz", hash = "sha256:7dbb285295097205bebbcb11f55161e5faa02111108fb7b17536176e31971eb0", size = 37568, upload-time = "2026-06-19T16:20:12.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a8/1a7acdfeb474daa25704f38f20f26b5fa2111b768c6d1c1451417bf8e4a5/pyobjc_framework_corebluetooth-12.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41dd9c9b4390e7316d189ba32d624ce933f7b25d351268b6e8cbea44f65e55f5", size = 13195, upload-time = "2026-06-19T16:08:25.759Z" }, + { url = "https://files.pythonhosted.org/packages/00/ad/de57d9060e4c5e9ca1a9bdd5b6bd1bdb73b198c0c53953cac445d9b3a84b/pyobjc_framework_corebluetooth-12.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f11df7052fa2d0524a9dadbc9c578fd3b8f3a350431edfa4ffa9e0a74b6faa32", size = 13195, upload-time = "2026-06-19T16:08:26.961Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c4/7938016860850e28c001dc9b7c653352c43f7aebfffc6d3c5fd087281f22/pyobjc_framework_corebluetooth-12.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2f8d2ed65c0e98ba044b6a7fc2f9a290a5c579a28d33a57c642f8617ccbcca0f", size = 13217, upload-time = "2026-06-19T16:08:27.802Z" }, +] + +[[package]] +name = "pyobjc-framework-libdispatch" +version = "12.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyobjc-core", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "pyobjc-framework-cocoa", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/3f/561653aff3f19873457c95c053f0298da517be89fdfc0ec35115ed5b7030/pyobjc_framework_libdispatch-12.2.1.tar.gz", hash = "sha256:0d24eda41c6c258135077f60d410e704bc7b5a67adcb2ca463918896c7363795", size = 40336, upload-time = "2026-06-19T16:20:56.371Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/33/362a92511af642d6168bf46369dbc3a98aa20fc2329768748f1e4d02786d/pyobjc_framework_libdispatch-12.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a47e8c53511eaf739f3972c48e0813bf8fd10dc88dc5543afceaec6554f5c4c2", size = 20486, upload-time = "2026-06-19T16:12:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b0/dc263ed1cee54badccaf60f061de1e25bea95504984401a22bee274b5f59/pyobjc_framework_libdispatch-12.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e295775c76eace23f60e53ef74b0f79429c665f91a870e4665c4b9887466efa", size = 20488, upload-time = "2026-06-19T16:12:49.441Z" }, + { url = "https://files.pythonhosted.org/packages/b3/8f/42cfa987c07a2b5ce8c236a42b0fb388b8807dac72c25e004cd4905ea9a3/pyobjc_framework_libdispatch-12.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8f41b5021ff70bc51220a79b41ebd1eacb55fe3ceb67448594f30e491a2c42a5", size = 15656, upload-time = "2026-06-19T16:12:50.361Z" }, +] + +[[package]] +name = "pyopengl" +version = "3.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/16/912b7225d56284859cd9a672827f18be43f8012f8b7b932bc4bd959a298e/pyopengl-3.1.10.tar.gz", hash = "sha256:c4a02d6866b54eb119c8e9b3fb04fa835a95ab802dd96607ab4cdb0012df8335", size = 1915580, upload-time = "2025-08-18T02:33:01.76Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/e4/1ba6f44e491c4eece978685230dde56b14d51a0365bc1b774ddaa94d14cd/pyopengl-3.1.10-py3-none-any.whl", hash = "sha256:794a943daced39300879e4e47bd94525280685f42dbb5a998d336cfff151d74f", size = 3194996, upload-time = "2025-08-18T02:32:59.902Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyquaternion" +version = "0.9.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/3d092aa20efaedacb89c3221a92c6491be5b28f618a2c36b52b53e7446c2/pyquaternion-0.9.9.tar.gz", hash = "sha256:b1f61af219cb2fe966b5fb79a192124f2e63a3f7a777ac3cadf2957b1a81bea8", size = 15530, upload-time = "2020-10-05T01:31:30.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/b3/d8482e8cacc8ea15a356efea13d22ce1c5914a9ee36622ba250523240bf2/pyquaternion-0.9.9-py3-none-any.whl", hash = "sha256:e65f6e3f7b1fdf1a9e23f82434334a1ae84f14223eee835190cd2e841f8172ec", size = 14361, upload-time = "2020-10-05T01:31:37.575Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +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/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]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + +[[package]] +name = "pyturbojpeg" +version = "1.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/e8/0cbd6e4f086a3b9261b2539ab5ddb1e3ba0c94d45b47832594d4b4607586/PyTurboJPEG-1.8.2.tar.gz", hash = "sha256:b7d9625bbb2121b923228fc70d0c2b010b386687501f5b50acec4501222e152b", size = 12694, upload-time = "2025-06-22T07:26:45.861Z" } + +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, +] + +[[package]] +name = "pywin32" +version = "312" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/1b/9cfdeac80ee45bebbbcb31f1b7b99a0d81a1c72de48d837be984e0e88b1d/pywin32-312-cp310-cp310-win32.whl", hash = "sha256:772235332b5d1024c696f11cea1ae4be7930f0a8b894bb43db14e3f435f1ff7e", size = 6361387, upload-time = "2026-06-04T07:49:14.329Z" }, + { url = "https://files.pythonhosted.org/packages/33/b1/7afc96d041d982c27bc2df6f853d43f01fd273e3d39d04be3647ddeb533d/pywin32-312-cp310-cp310-win_amd64.whl", hash = "sha256:5dbc35d2b5320dc07f25fa31269cfb767471002b17de5eb067d03da68c7cb2db", size = 6926780, upload-time = "2026-06-04T07:49:16.881Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/4140da9ad54108e517f4a16b2d83da3033e08662144623e1239587cb7db6/pywin32-312-cp310-cp310-win_arm64.whl", hash = "sha256:3020656e34f1cf7faeb7bccd2b84653a607c6ff0c55ada85e6487d61716deabd", size = 4307203, upload-time = "2026-06-04T07:49:18.993Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f5/10a6e845a00fc5e7afd0a988b744f403d4d57162a28d160a093c4d9322f0/pywin32-312-cp311-cp311-win32.whl", hash = "sha256:17948aeadbdb091f0ced6ef0841620794e68327b94ee415571c1203594b7215c", size = 6362659, upload-time = "2026-06-04T07:49:21.349Z" }, + { url = "https://files.pythonhosted.org/packages/35/c4/dcd2d62b5944b6d5db53413a5899016ccd57ffcb7278f3f81655d25d2027/pywin32-312-cp311-cp311-win_amd64.whl", hash = "sha256:d11417d84412f859b722fad0841b3614459ed0047f7542d8362e77884f6b6e8a", size = 6928825, upload-time = "2026-06-04T07:49:23.934Z" }, + { url = "https://files.pythonhosted.org/packages/b7/56/3cbb433fe4501cdba2eb9040f56a4e1a8243faa4186b25295564d1a7a79d/pywin32-312-cp311-cp311-win_arm64.whl", hash = "sha256:b2200a054ca6d6625c4842fc56a4976a4b47f96b73dbe5538c3f813a80359f47", size = 6721875, upload-time = "2026-06-04T07:49:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/83/ff/32aa7d2ed0ab12b323aaa64f9b75e6ad4f8fd09f9ccfc28c79414d46838d/pywin32-312-cp312-cp312-win32.whl", hash = "sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b", size = 6371877, upload-time = "2026-06-04T07:49:28.836Z" }, + { url = "https://files.pythonhosted.org/packages/03/d9/77040d3b43df3f3be32ea289433d660d2727f5ba327bc73be835127d9d60/pywin32-312-cp312-cp312-win_amd64.whl", hash = "sha256:b457f6d628a47e8a7346ce22acb7e1a46a4a78b52e1d17e1af56871bd19a93bc", size = 6914841, upload-time = "2026-06-04T07:49:31.85Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cc/7b1ec671775756020a0ee7f4feeaf3c568f0ab86bd3900088cf986937a92/pywin32-312-cp312-cp312-win_arm64.whl", hash = "sha256:6017c58e12f6809fbb0555b75df144c2922a9ffd18e4b9b5afa863b6c1a9d950", size = 6727901, upload-time = "2026-06-04T07:49:34.244Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, +] + +[[package]] +name = "reactivex" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/af/38a4b62468e4c5bd50acf511d86fe62e65a466aa6abb55b1d59a4a9e57f3/reactivex-4.1.0.tar.gz", hash = "sha256:c7499e3c802bccaa20839b3e17355a7d939573fded3f38ba3d4796278a169a3d", size = 113482, upload-time = "2025-11-05T21:44:24.557Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/9e/3c2f5d3abb6c5d82f7696e1e3c69b7279049e928596ce82ed25ca97a08f3/reactivex-4.1.0-py3-none-any.whl", hash = "sha256:485750ec8d9b34bcc8ff4318971d234dc4f595058a1b4435a74aefef4b2bc9bd", size = 218588, upload-time = "2025-11-05T21:44:23.015Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions" }, +] +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 = "regex" +version = "2026.6.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/05/e4f219230e11e774a6c9987d2ab0d0c6b8573e13a17e143d0015bee710ef/regex-2026.6.28.tar.gz", hash = "sha256:3cb4b6c5cb3060cc31efdc1fbb27c25fb9b29044afd87e40601a1c4d9db54342", size = 416101, upload-time = "2026-06-28T19:56:55.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/dc/f7a8c9cf0768f704153d358fae2bc883199bc4ea1e4aa458f1be9d0ef2ce/regex-2026.6.28-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b83932645630965fd860fdb70ebbf964bf3e8007f08851ea424d01f8d35454a8", size = 489471, upload-time = "2026-06-28T19:53:06.385Z" }, + { url = "https://files.pythonhosted.org/packages/44/b3/9786a4a2133e2f1cc5897ed3d2da3da29ff54b775ffa38bc5935fc24be82/regex-2026.6.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e81f1952355042e517dc9861ce65c676e4a098f42402993c40461786d1f794d4", size = 291294, upload-time = "2026-06-28T19:53:09.232Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1f/bfe5b529257f0853aa6b94146e0f6462f4d45aa4f3c05d5a828f415dfd40/regex-2026.6.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2097591101d70bcc108af64c46f6066bb698ee067fec5f75beac0be317639311", size = 289216, upload-time = "2026-06-28T19:53:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/25/56/f615165e90ac5f3b72b249240643439520bbac0ac60a9de06868528eba4c/regex-2026.6.28-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:31d7538a614b5842bf53ce329d07b43f97754ca7e6db8d69f347e071bce1c953", size = 784787, upload-time = "2026-06-28T19:53:12.393Z" }, + { url = "https://files.pythonhosted.org/packages/04/94/c9e3ad31b3d5fbe1228fee8319e0c02a5460296624f220d08764547fe6ae/regex-2026.6.28-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5561e47bbe2b75373b695326507743fcdd4d2cc7f5022312024ccf39fa094e0", size = 852137, upload-time = "2026-06-28T19:53:14.287Z" }, + { url = "https://files.pythonhosted.org/packages/c0/77/d506a428e446466ee298f5425a774737d0671d070425ed794bb3314d60c6/regex-2026.6.28-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c10f2c5a55ab3dd8318d8ad5f11b530e2691c0edebebde7713066f484902c3fb", size = 899525, upload-time = "2026-06-28T19:53:15.987Z" }, + { url = "https://files.pythonhosted.org/packages/aa/72/becc00d839f19401f10a20168b44711c7b02f7f62bba875b2d8f98417435/regex-2026.6.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23f7e0cc60c72486b42a685f1ff4eec90d50d4fb05e4f9c7d5363b03aa02600d", size = 794116, upload-time = "2026-06-28T19:53:17.372Z" }, + { url = "https://files.pythonhosted.org/packages/fa/11/ea2ca423eeaac2e18077a18b058614e9201f130750df2126d444e39acab2/regex-2026.6.28-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:76493755f79a88d5ed2c9e63a41d3c05997e0a7ffbe76ed8c4ded8be35b8b14c", size = 786257, upload-time = "2026-06-28T19:53:18.712Z" }, + { url = "https://files.pythonhosted.org/packages/6d/9e/f5bf7ecbd14ff2086f015c54dc24fd0d74ba5327fef0de479213f8128615/regex-2026.6.28-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ff0f41a00f23ea5054acb61901380c41813d813eee3f80f800995710bcc52ecd", size = 769914, upload-time = "2026-06-28T19:53:20.564Z" }, + { url = "https://files.pythonhosted.org/packages/43/04/f9040a5360a06241ba5b7f2e6f1c6184e104a84e6f6522535700e94bf8e2/regex-2026.6.28-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3c60b297292e7e1ef5d02a4759f9e452ee4c8bb95e168d8fd0b5db01bd806f9f", size = 775013, upload-time = "2026-06-28T19:53:22.067Z" }, + { url = "https://files.pythonhosted.org/packages/73/97/4e46f7abf2f864319d2bcac609af3c0532968c66a3364337778fd232b83c/regex-2026.6.28-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7cf03c87f7b9cbc25a8894cf9be83818406677b6b391b003ec7c884923387b5", size = 848814, upload-time = "2026-06-28T19:53:24.575Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b8/3d1f995727799a1e2e693e397acb7358094606e5591b6b5fd3128d2d1409/regex-2026.6.28-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:418208ea0af51cfed4f46eb9b1ea7cfc990ca284f0084ecbd951460fb089421e", size = 757702, upload-time = "2026-06-28T19:53:26.215Z" }, + { url = "https://files.pythonhosted.org/packages/20/10/fd5653b8572910a4fe9055f8959b070d7d9443c94ce986529fcdb5fb2a3c/regex-2026.6.28-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:7635fa2cddb917a6bbfac7890602573d2d8c4e470703b0640e6f86a988817ec3", size = 837140, upload-time = "2026-06-28T19:53:27.655Z" }, + { url = "https://files.pythonhosted.org/packages/5d/31/da77e3ef7b594a2aacbd03ce3d0050f33ab3e021df50c6901467c9006511/regex-2026.6.28-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7bb96c13d6cf5880d31bbef84ca701a64d738aa491c2b79975cc33f8ad00a31e", size = 782105, upload-time = "2026-06-28T19:53:29.375Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4d/c379001448d0f58b6946f168d4af96ad60a16c1553259c27b0df8701b640/regex-2026.6.28-cp310-cp310-win32.whl", hash = "sha256:56f05194c4843957dd8b3af87eb0c52d8cf0509e7f18e172d727f5f8ff840646", size = 266728, upload-time = "2026-06-28T19:53:31.813Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8f/cb656529efa87d74cce0d69e606c745537016da3bdfae78f342af2242ee3/regex-2026.6.28-cp310-cp310-win_amd64.whl", hash = "sha256:70710927033af3b54369f17aaba1343b97a23d0b1aa994fa1512b08b1b8c136a", size = 277901, upload-time = "2026-06-28T19:53:33.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ac/d35ccc309c9409406445ab2ef0b56f6a341a916ccff49ff9ac5cc6bb8e9b/regex-2026.6.28-cp310-cp310-win_arm64.whl", hash = "sha256:ed7b30185ee3f8b9b053b0be567b4d226016e2afbebc17fde1c6a4580937b688", size = 276880, upload-time = "2026-06-28T19:53:35.029Z" }, + { url = "https://files.pythonhosted.org/packages/72/db/9051b36294bdbabaa9c7db57db0fbcdfbd17f7a106c539bb423d0323faea/regex-2026.6.28-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a71b51dd08b9b62f055fafab3dee8af8bd2ec81b373a44caef18d6c5ca28f43a", size = 489481, upload-time = "2026-06-28T19:53:36.684Z" }, + { url = "https://files.pythonhosted.org/packages/35/3f/24097a3c3ff30f9a639888900faaecabcf5f54a5bc9c851c297e11b349ef/regex-2026.6.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9c26a47770d30a0f85c01e261d2a3ebc342c4af6fd666dbd8c1fe4cbf3adf726", size = 291292, upload-time = "2026-06-28T19:53:38.39Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cc/e0d762a189cfb4e8926d16e691720690d139a977b38fdb80230c259332ab/regex-2026.6.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e5efbc1af38f97e300d43028e5a92e752d924bcfb7f465d8669d5d5a6e78c233", size = 289232, upload-time = "2026-06-28T19:53:40.181Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c8/ca0ac7f09cc88ca61e0c61c53f7db29334f660ffba5d0b52378e7c44723c/regex-2026.6.28-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1758df6fdd8c800620a5638958720e8a635e1da49a2f09df2dd63e94a24ec4a", size = 792332, upload-time = "2026-06-28T19:53:41.782Z" }, + { url = "https://files.pythonhosted.org/packages/8e/92/04ae94cbe0dd1f478b2aef6c46f995bb6946d3e338d4b28605478b66a2b7/regex-2026.6.28-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ad73ecf20c1ef5c975639f8bf845a9370fcf7dada7edc1e3b0bca20e2f8202f6", size = 861743, upload-time = "2026-06-28T19:53:43.261Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ec/024d7638c807679ff8a0e6081d01d66c7762339af1cac71e45911587ff9a/regex-2026.6.28-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4d80c798b0eec6ea3d45f8816a1e8886c5664615d347d89e8c075b576a1b5a5d", size = 906481, upload-time = "2026-06-28T19:53:44.948Z" }, + { url = "https://files.pythonhosted.org/packages/cd/fd/93bfe5af45f0be4fa8983945455c0e6924e1aeb879cde227958869c1e71c/regex-2026.6.28-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a361feeaf1b6ba1df060f2ff5c5947092edf537a35ce78e76387ac56d3e0f4a4", size = 799867, upload-time = "2026-06-28T19:53:46.997Z" }, + { url = "https://files.pythonhosted.org/packages/ee/fd/e5d965d41f2398c8ce0f37a4652f03bb297fd009bb796d390134225dda12/regex-2026.6.28-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8b92366d9c8bba9642989534073662abdd9b41faf7603a7ae71597833f3b88f0", size = 773632, upload-time = "2026-06-28T19:53:48.892Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d9/ff39afaec92b9ee2dba0302a4783976005091681069808938c31cf8df3b6/regex-2026.6.28-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:11251768cc23f097dd61b18f67966e70f74da822784d17e12a444eb6b29d4288", size = 781669, upload-time = "2026-06-28T19:53:50.693Z" }, + { url = "https://files.pythonhosted.org/packages/45/4e/e2fd4bb8228e10c24af2d7ff867182372190e498eab9fd29cbe54c403c95/regex-2026.6.28-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad5c67786145ec28a71a267d9f9d92bdc8d70d65541eea852c253f520a01f918", size = 854497, upload-time = "2026-06-28T19:53:52.323Z" }, + { url = "https://files.pythonhosted.org/packages/72/7c/f0340384a973082979064156d05f3d2cc1dced7371efcd7a1b45726a1a8a/regex-2026.6.28-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f1da438e739765c3e85175ede05816cbede3caaacb1e0680568bda6119bfdfca", size = 763335, upload-time = "2026-06-28T19:53:54.024Z" }, + { url = "https://files.pythonhosted.org/packages/e1/32/90ce0d0898e205506cc22b9c81cfb16b722e06ca5f50fad51c053c2a727b/regex-2026.6.28-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d98b639046e51c5de64d9f77351532105e99ca271cb6f7640e1f903d6ab63032", size = 844615, upload-time = "2026-06-28T19:53:56.216Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/55abb149599dce1ade687170557129524011eeb3d92afe02429cea7754a2/regex-2026.6.28-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e164ace4dbab5c6ad4a4ac7c41a2638fe226d0c770a86f2eb041f594bac6ee7", size = 789193, upload-time = "2026-06-28T19:53:57.791Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ea/cf7f6f6f152e52fdad978b913bf24c14df647eca0f81ef31f3aee0be8982/regex-2026.6.28-cp311-cp311-win32.whl", hash = "sha256:3169a3159e4d99d9ae85ff0ed90ef3b8906cc3152653b6078b842ace6c8f72c3", size = 266731, upload-time = "2026-06-28T19:53:59.938Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cf/a48d8e8d406b22481cad146f48fa0dfca3c5f402b91f26d8e5a0fe4f513d/regex-2026.6.28-cp311-cp311-win_amd64.whl", hash = "sha256:5977295b0a74e8241df8a4b3b27b12412a831f6fa32ee8b755039592cd768c3d", size = 277918, upload-time = "2026-06-28T19:54:01.502Z" }, + { url = "https://files.pythonhosted.org/packages/89/b2/a222392207db7ed86281a732a99f7cf7f2bb35d332799e892b8510be000e/regex-2026.6.28-cp311-cp311-win_arm64.whl", hash = "sha256:f5fbaef40c3e9282ccee4b075f5600a0d858aa0c34147732f1baa69c8188a95d", size = 276876, upload-time = "2026-06-28T19:54:03.411Z" }, + { url = "https://files.pythonhosted.org/packages/da/21/44aa415873032056c43eac21c67285deb2cf66cddb2a964c3cdc8f803efc/regex-2026.6.28-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:81cc5793ad33a10444445e8d29d3c73e752c8fb2e120772d70fcb6d41df40fe1", size = 490480, upload-time = "2026-06-28T19:54:05.392Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5f/30d4116093c2128099f78b6990dfc1698fdbf3ee528f1e1c647378034c79/regex-2026.6.28-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e18225243250a1f7d7e5e5d883f3b96465cd79031acf5c6db902b7025f2125d9", size = 292137, upload-time = "2026-06-28T19:54:07.088Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/ca20a0e0de49837e6337603a91ab77556aa27033ac5b975615d98698cfb3/regex-2026.6.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ecd1638b1c2db1f2d01c182a4b0d3e2e88b0e99910320a745c1727ee3638ddab", size = 289623, upload-time = "2026-06-28T19:54:08.762Z" }, + { url = "https://files.pythonhosted.org/packages/50/11/c013422a7e2c59946df8ac93e792a4922c98287f2a2181341603c78a5d98/regex-2026.6.28-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4303ebe16b74eeb3fe2715745023266fea92fd44a23f3e7bb2fb48c7a7bbc195", size = 796756, upload-time = "2026-06-28T19:54:10.616Z" }, + { url = "https://files.pythonhosted.org/packages/b0/95/1309645a0e1ee6fb91d954501da57a0b33d50ad2a9acb313702851a7054e/regex-2026.6.28-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56b856b70b96c381d837f609eee442a1bd320cd2159f5c294b679552fb1a7eaf", size = 865465, upload-time = "2026-06-28T19:54:12.742Z" }, + { url = "https://files.pythonhosted.org/packages/20/06/491802db47c6f5e2904ffa2518ad3ac27fe6bbf5a66d73210a95cc080d47/regex-2026.6.28-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f74675ab76ab1d005ffba4dee308e53e89efc22be6e9f9fae5b539a3f81bdff2", size = 912350, upload-time = "2026-06-28T19:54:14.508Z" }, + { url = "https://files.pythonhosted.org/packages/5e/60/3ba57840bcc7e2367090360de0c15a5ba6ad22be89314251105f2e943f43/regex-2026.6.28-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90581684565a93f7258af1e5d3f41ef20d7d7c61f2a428183a342bcb65485e38", size = 801261, upload-time = "2026-06-28T19:54:16.432Z" }, + { url = "https://files.pythonhosted.org/packages/eb/27/af1eb74e9a78c782b3e450b611a595e44906da8a5107e1227f4a7fd0480b/regex-2026.6.28-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:28f9e6c28f9b90f6f784595a33240a57e181e61b6ee3dc259b25c61e356d1aa3", size = 777072, upload-time = "2026-06-28T19:54:18.128Z" }, + { url = "https://files.pythonhosted.org/packages/20/18/fdd4c883a39e3ed00d669062af1135809bfd3281bf528150849fbd68825b/regex-2026.6.28-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:378a71d861fc7c8806b04ac5b133d53c0e774f92f5d9663a539872d3fa2b0417", size = 785119, upload-time = "2026-06-28T19:54:20.314Z" }, + { url = "https://files.pythonhosted.org/packages/1c/79/0aabe34b8482dcadf64355f70f96e22eba5ec6c1efb33563f89654f4061c/regex-2026.6.28-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4cc199874ecd6267a49b111052250825bfe19b5101b23b2ba80f54efa3e0994e", size = 860118, upload-time = "2026-06-28T19:54:22.368Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2c/c973323306a27c9db7d160e9584eb7e0ece2a96224ccb0d39060558b31f9/regex-2026.6.28-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b916a10431494ef4b4d62c6c89cab6426af7873125b8cd6c15811bf5fc58eec8", size = 765786, upload-time = "2026-06-28T19:54:24.265Z" }, + { url = "https://files.pythonhosted.org/packages/e3/df/9ca3e378e352242a4cb45573a5e9162c3ee791507702a23966fa559e36b5/regex-2026.6.28-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2e27727fba075f1e4409416d2f537d4c30fc11f012ea507f7bd74d3e19ecb57a", size = 852120, upload-time = "2026-06-28T19:54:25.972Z" }, + { url = "https://files.pythonhosted.org/packages/a2/3e/3e31e255c4971f53cbce6306b5e3c76cbd3735a54f419bb3b2f194e9f68c/regex-2026.6.28-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:700fc6a7844bb2c4149292ac79d1df8841a00acd4d45cd32c1ebc7bcc1fd0da8", size = 789503, upload-time = "2026-06-28T19:54:27.678Z" }, + { url = "https://files.pythonhosted.org/packages/72/01/d36561c21c3033d7eeb31d51b491916817de7861acefccc5fc9db8a5037c/regex-2026.6.28-cp312-cp312-win32.whl", hash = "sha256:03376d60b6a11aecb88a79fa2be06b40faa01c6693bc31ef69435cd4818b9463", size = 267109, upload-time = "2026-06-28T19:54:29.316Z" }, + { url = "https://files.pythonhosted.org/packages/a0/59/bbbb0591f38b18c65977cd65ce64749eba1c1996c99ac04e900fc30c0dcb/regex-2026.6.28-cp312-cp312-win_amd64.whl", hash = "sha256:fbd2ded482bf99e6651992bbfcde460272724d4bbc49ef3d6b46d9312867ec84", size = 277711, upload-time = "2026-06-28T19:54:31.143Z" }, + { url = "https://files.pythonhosted.org/packages/86/06/be4f6b337d773ae5739a1bc238f97c16926e72017243735853c030f4c628/regex-2026.6.28-cp312-cp312-win_arm64.whl", hash = "sha256:37294d3d7ddb64c7e89184b2894e0f8f0a19c514bc59513d71fe692c3a8d5fc6", size = 277022, upload-time = "2026-06-28T19:54:32.97Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rerun-sdk" +version = "0.32.0a1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, + { name = "pyarrow" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/e0/5d92c02d6ca63b5692e72ad3c5b89e32d44c3a6a99a74be3cb9a9d2c9741/rerun_sdk-0.32.0a1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:704e031c4ed3eb83837ce37b73741082ab669a2b4c88348a08ba61e3ba6d8656", size = 123843351, upload-time = "2026-04-29T18:59:44.469Z" }, + { url = "https://files.pythonhosted.org/packages/ab/97/1daa4751a281bdb6cda3473229b2746779093ffaba70dd868a3074ef3392/rerun_sdk-0.32.0a1-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0a821b30f287dabbf694aa1715e4dad0e31990cf4b612757c895f096885512e9", size = 133447478, upload-time = "2026-04-29T18:59:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/84170e6722a09294b57d578fc1d01266c6e78408a544876f5271ecfa66a9/rerun_sdk-0.32.0a1-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:8045beb820372e092a6cf100fb87091fa804168e5cf8a0c29f97cdee56b134d8", size = 137724590, upload-time = "2026-04-29T18:59:55.238Z" }, + { url = "https://files.pythonhosted.org/packages/ba/2f/09845dcdf14047cc8118566572628c533f57f6a232ee0b3fd61283b153e8/rerun_sdk-0.32.0a1-cp310-abi3-win_amd64.whl", hash = "sha256:1d6f8c3d50699ae075551a62e971730870a685cb74189b550a348561f37f9fe5", size = 118766780, upload-time = "2026-04-29T19:00:00.121Z" }, +] + +[[package]] +name = "retrying" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/5a/b17e1e257d3e6f2e7758930e1256832c9ddd576f8631781e6a072914befa/retrying-1.4.2.tar.gz", hash = "sha256:d102e75d53d8d30b88562d45361d6c6c934da06fab31bd81c0420acb97a8ba39", size = 11411, upload-time = "2025-08-03T03:35:25.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/f3/6cd296376653270ac1b423bb30bd70942d9916b6978c6f40472d6ac038e7/retrying-1.4.2-py3-none-any.whl", hash = "sha256:bbc004aeb542a74f3569aeddf42a2516efefcdaff90df0eb38fbfbf19f179f59", size = 10859, upload-time = "2025-08-03T03:35:23.829Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "robomimic" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "egl-probe" }, + { name = "h5py" }, + { name = "imageio" }, + { name = "imageio-ffmpeg" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "psutil" }, + { name = "tensorboard" }, + { name = "tensorboardx" }, + { name = "termcolor" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/c3/44b1d1ea4bcb4bbed43d19e09505f4142714451ded74020d4f679cdc89fb/robomimic-0.2.0.tar.gz", hash = "sha256:ee3bb5cf9c3e1feead6b57b43c5db738fd0a8e0c015fdf6419808af8fffdc463", size = 192919, upload-time = "2021-12-17T19:00:33.279Z" } + +[[package]] +name = "robosuite" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mujoco" }, + { name = "numba" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "opencv-python" }, + { name = "pillow" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/a1/9dd07a9a5e09c6aa032faf531da985808b34437cbf6c8f358fe8f7c47118/robosuite-1.4.0.tar.gz", hash = "sha256:a8a6233d7458dbd91bf00a86cab15aa1c178bd9d1b28d515db2cf3d152cb48e6", size = 192182147, upload-time = "2022-12-01T07:31:55.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/08/fe231064caaf2766d47818ca12fd8acf10dc4e762a33dabc6293e83bfead/robosuite-1.4.0-py3-none-any.whl", hash = "sha256:aba065e7b36745738cede259457b2cb349427f3608728d867ef3a2034cb62994", size = 193477875, upload-time = "2022-12-01T07:28:53.457Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +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/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.6.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/1f/a2dca5ffdbf1d475ffc4e80e4d5d720ff3a00f691795910116960ee12511/rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7", size = 342174, upload-time = "2026-06-30T07:14:54.821Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/323d08583c0832911768663d1944f0107fcd4088704858d84b5e06d105a0/rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911", size = 345513, upload-time = "2026-06-30T07:14:56.515Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2a/e31989834d18d2f26ec1d2774c5b1eb3331df4ea8ada525175294c94b48a/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4", size = 373783, upload-time = "2026-06-30T07:14:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/fe/e80107ee3639585c9941c17d6a42cd65325022f656c023191fce78c324c8/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261", size = 378316, upload-time = "2026-06-30T07:14:59.077Z" }, + { url = "https://files.pythonhosted.org/packages/22/6f/81e3adf81acfb6fa694de2a6e4e7d8863121e3e0799e0a7725e6cf5679c4/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278", size = 499423, upload-time = "2026-06-30T07:15:00.488Z" }, + { url = "https://files.pythonhosted.org/packages/2d/9a/41263969df0ce3d9af2a96d5005a288200af1989aed3354bfceb5fc0b21f/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9", size = 386077, upload-time = "2026-06-30T07:15:01.911Z" }, + { url = "https://files.pythonhosted.org/packages/5e/19/7e98f468bd50346faff5b10e5297374b443bfdddacc8e9fbc65984539597/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7", size = 371315, upload-time = "2026-06-30T07:15:03.317Z" }, + { url = "https://files.pythonhosted.org/packages/99/3c/2b973b4d371906a134b03decfea7f5d9835a2c6d263454392e15b64b5b18/rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3", size = 383502, upload-time = "2026-06-30T07:15:04.627Z" }, + { url = "https://files.pythonhosted.org/packages/98/2a/12e2799500af0a307bca76b63361c51f9fe479223561489c29eea1f2ee41/rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da", size = 402673, upload-time = "2026-06-30T07:15:05.856Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e3/21e5872d165fe08be4f229e3d5ee9d90019c0bf0e5538de60dbd54009450/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4", size = 549964, upload-time = "2026-06-30T07:15:07.159Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d0/5ee0fe36844297de8123bee27bc12078c1a7416ad9f1b8a8ca18d6b0c0ac/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6", size = 615446, upload-time = "2026-06-30T07:15:08.531Z" }, + { url = "https://files.pythonhosted.org/packages/b1/80/1ea5873cb683f2fbe5f21b23ea1f6d179ead19f3c5b249b7eb5dca568ef2/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93", size = 576975, upload-time = "2026-06-30T07:15:09.97Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e1/90ef639217a5ddb15b7f4f61b1c33911fd044ad03c311bafdd2bcab85582/rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a", size = 204453, upload-time = "2026-06-30T07:15:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/f2/b7/b7a1695d7af36f521fb11e80d6d3adbd744f73b921859bd3c2a2c0dc706f/rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127", size = 223219, upload-time = "2026-06-30T07:15:12.476Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a2/145afacf796e4506062825941176ad9445c2dcf2b3b6a1f13d3030a15e19/rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804", size = 219137, upload-time = "2026-06-30T07:15:13.631Z" }, + { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" }, + { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" }, + { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" }, + { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" }, + { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" }, + { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9c/f0d19ac587fd0e4ab6b72cda355e9c5a6166b01ef7e064e437aef8eb9fef/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f", size = 349791, upload-time = "2026-06-30T07:17:33.315Z" }, + { url = "https://files.pythonhosted.org/packages/38/c7/1d49d204c9fd2ee6c537601dc4c1ba921e03363ca576bfab94a00254ac9a/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171", size = 352842, upload-time = "2026-06-30T07:17:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e5/c0b5dc93cd0d4c06ce1f438907649514e2ea077bcd911e3154a51e96c38e/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90", size = 382094, upload-time = "2026-06-30T07:17:36.514Z" }, + { url = "https://files.pythonhosted.org/packages/0d/54/ec0e907b4ca8d541112db352409bd15f871c9b243e0c92c9b5a46ae96f01/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca", size = 388662, upload-time = "2026-06-30T07:17:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f4/921c22a4fd0f1c1ac13a3996ffbf0aa67951e2c8ad0d1d9574938a2932e8/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9", size = 504896, upload-time = "2026-06-30T07:17:39.689Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1b/a114b972cefa1ab1cdb3c7bb177cd3844a12826c507c722d3a73516dbbaf/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c", size = 391545, upload-time = "2026-06-30T07:17:41.336Z" }, + { url = "https://files.pythonhosted.org/packages/4e/98/af9b3db77d47fcbe6c8c1f36e2c2147ec70292819e99c325f871584a1c11/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9", size = 380059, upload-time = "2026-06-30T07:17:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ba/0efd8668b97c1d26a61566386c636a7a7a09829e474fdf807caa15a2c844/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41", size = 393235, upload-time = "2026-06-30T07:17:44.637Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/8c139ee9690f73b0829f32647de6f40d826f8f443af6fa72644f96351aac/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c", size = 413008, upload-time = "2026-06-30T07:17:46.225Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/0043896fdd7828ce09a1d9a8b06433714d0960fc4ff3fc4aa72b666b764e/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9", size = 558118, upload-time = "2026-06-30T07:17:47.759Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/02355f0e134f783a8f9814c4680a1bd311d37671577a5964ea838573ff37/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76", size = 623138, upload-time = "2026-06-30T07:17:49.355Z" }, + { url = "https://files.pythonhosted.org/packages/10/85/48f0abdcef5cce4e034c7a5b0ceeceba0b01bf0d942824f4bb720afe2dec/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826", size = 586486, upload-time = "2026-06-30T07:17:51.141Z" }, +] + +[[package]] +name = "safetensors" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/06/f955dbbb1859e3bd23c8ac6141af5106e7ad5fedec4a3a6e3d60f94b7001/safetensors-0.8.0.tar.gz", hash = "sha256:fabaf3e0f18a6618d9b36560682562157f77c2b71fcffc7b432be2baed9d753d", size = 325846, upload-time = "2026-06-09T07:52:25.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/a0/f718cda65b05407d228f97602cf60dca269c979867aa5beb25410de26cd3/safetensors-0.8.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c554f85858e05226d3c2828e32395e677434685d6d94594a41643361c5e837f0", size = 473568, upload-time = "2026-06-09T07:52:18.829Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b1/fa7c600e7dceae12e9606c7578cbc9ff1e1ed55844883ee5c92205e86226/safetensors-0.8.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c80201d22cbf405b80647a60ada77bba06c8fba2da2743ba1e89cdcc39a81f25", size = 484562, upload-time = "2026-06-09T07:52:17.518Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/65a7de0af421317bb36a067241e4235fff194eed60b961ed6d3f59a3fc60/safetensors-0.8.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a46e5ff292c356d6991e60942ba7f79817682d3a2cef0702136448cb9c4d235", size = 502844, upload-time = "2026-06-09T07:52:07.624Z" }, + { url = "https://files.pythonhosted.org/packages/91/4f/3175c9d75634e0e0dda0082794193521035edd7c70a6f212bf33ca06ddf4/safetensors-0.8.0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4124502b78f03534117c848f87a39b8f31e577b15eff423bf8bfb95f2a8c30d0", size = 511823, upload-time = "2026-06-09T07:52:09.565Z" }, + { url = "https://files.pythonhosted.org/packages/20/87/846c289e7aa2299eff406335717cf43ce8777194ece8aad75772e0411615/safetensors-0.8.0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bc0a787ba8a35be368ee3574edfa2b1ad389eebd0a72e482ae275490e3f6c98", size = 633461, upload-time = "2026-06-09T07:52:11.128Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/8d64d9df2c45d5ded401df889d0ad90882804ca172d79ec4f0df8f727fe0/safetensors-0.8.0-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040070828e36dc8e122178bbbd5830ff9e97920affb84cbe0f46442497bed358", size = 545148, upload-time = "2026-06-09T07:52:13.603Z" }, + { url = "https://files.pythonhosted.org/packages/28/50/f203ff3a3ddfe19308efc83c5a3a29ed02bf786732ec35e68bf9162f3365/safetensors-0.8.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd6f3f93c9a0a7cc2788ee63fb763353d4bd2e89b0751bc78fcf7dda00bea774", size = 516040, upload-time = "2026-06-09T07:52:16.29Z" }, + { url = "https://files.pythonhosted.org/packages/46/fb/cdaed17ceb2948784fd9c36b6fd3e951b608547cea81a48e8ee6f8cfdfcb/safetensors-0.8.0-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:fcdd41ec4628fee5799f807c73c353629130fbd942aa23d83c623dd6c9d52d78", size = 513832, upload-time = "2026-06-09T07:52:12.37Z" }, + { url = "https://files.pythonhosted.org/packages/0d/49/1e15de264dcc3b77943d2d0c56a95809956883b1c2d6d585c792523f180b/safetensors-0.8.0-cp310-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e9f537aa183a38ace122d27303dcd986b26bd2a7591f9181d7f0c396f4677ca", size = 559930, upload-time = "2026-06-09T07:52:14.743Z" }, + { url = "https://files.pythonhosted.org/packages/2a/43/bf38443278eab4b1be1fce2931e2b012ad9cb7df52ada751d0aab8f7659a/safetensors-0.8.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87eec7ffed2b809f05a398a8becb7d013f19f7837cd15d9748580d6cf30dbaf4", size = 678670, upload-time = "2026-06-09T07:52:20.032Z" }, + { url = "https://files.pythonhosted.org/packages/72/e3/68cd3fa5b48488e84add63e04cb12f3bc28ae4638c06d4508c6e88823d0e/safetensors-0.8.0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:4a95ae2b05d7726d751da4ebf626a2ca782b706e101bd894c95bc2450b1cffcc", size = 786679, upload-time = "2026-06-09T07:52:21.322Z" }, + { url = "https://files.pythonhosted.org/packages/29/4b/1c19c509d56e01f4fbb3d0a2e597450f6cc04d1d56cf52defb0a62dfd715/safetensors-0.8.0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae091f16662658bdc019a4ff6cb4c085bb7d725eb5978b183ffd265863b6d2d", size = 765683, upload-time = "2026-06-09T07:52:22.594Z" }, + { url = "https://files.pythonhosted.org/packages/27/43/41c1621732edd934d868a00d1b891584c892a7b62a9aab82ea5a0a5623ee/safetensors-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8e080062fcde23be189565e1c3305d16751a218ecf9412c8601e64204eb6f846", size = 722361, upload-time = "2026-06-09T07:52:23.924Z" }, + { url = "https://files.pythonhosted.org/packages/8e/3f/73ccf82579412b4a71c4ca673f10b5f1f888d7cf5af7fe24f27d30307be4/safetensors-0.8.0-cp310-abi3-win32.whl", hash = "sha256:2ddf52eac562eda224f99acfa7889d02968c1fd59a5b011ae7d8137c37e9c02d", size = 342401, upload-time = "2026-06-09T07:52:28.895Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6d/3fba214c1e5e0f69991677ec3bc17023f0421776975e1de0c682dca475e2/safetensors-0.8.0-cp310-abi3-win_amd64.whl", hash = "sha256:096ec1a98435df7beb08853bb5aa9081a84f23d0adc67ed1a0a10550f608373f", size = 355540, upload-time = "2026-06-09T07:52:27.832Z" }, + { url = "https://files.pythonhosted.org/packages/8d/fc/7eedc3510d97878876e32774eebbeb61c43f148a96e915c84229a3e967aa/safetensors-0.8.0-cp310-abi3-win_arm64.whl", hash = "sha256:f7838e5135a406ad3e02efdcb8cf2e5397d368b0154537c4fec682dbc544d452", size = 340500, upload-time = "2026-06-09T07:52:26.745Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "joblib", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, + { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, + { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, + { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, + { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, + { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, + { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "joblib", marker = "python_full_version >= '3.11'" }, + { name = "narwhals", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/6f/37092bdb25f712817231799fc5674d8e704066a8a70c1d2d40517e18b4ab/scikit_learn-1.9.0.tar.gz", hash = "sha256:8833266989d3a5110178a9fae30783675460724d0e1efb13b14901d2c660c557", size = 7750767, upload-time = "2026-06-02T11:54:32.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/be/e844fd9586e66540a15b71924d17a6cbc1bb749e81ddd0a796bcdba4c055/scikit_learn-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9db6f4d34e68c8899e4cab27fdf8eafe6ed21f2ba52ceb25ea250cd237f8e47b", size = 8789686, upload-time = "2026-06-02T11:53:05.439Z" }, + { url = "https://files.pythonhosted.org/packages/42/e2/ff880f62677a17d035817d543cb0fc8727d01eccbee81c5f7fc733a9d856/scikit_learn-1.9.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f401448645a3e7bc115aa3c094097865155b34bff1cba8101857d9104e99074c", size = 8256782, upload-time = "2026-06-02T11:53:08.904Z" }, + { url = "https://files.pythonhosted.org/packages/25/64/eb40435e1a508ab1b4e284ce43ae80f6a162e5be5e38ed5a6fab467a9ea4/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd3a8ef0c758555a3b23c03adaa858af32f7736785ded50ad5991f59c4ed03fa", size = 8992419, upload-time = "2026-06-02T11:53:11.551Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/4810a28e473185429e45a57eebcc91fc991b33d889cc0676063e671db03d/scikit_learn-1.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7e254636164090da847715a27f8e5478feb98c40a9e0ee90cbd277de9e5ceb8", size = 9281411, upload-time = "2026-06-02T11:53:15.063Z" }, + { url = "https://files.pythonhosted.org/packages/3b/67/be3d369f40d8178ba3bd86635d132e08cb5329b023e4669d9426d84bc007/scikit_learn-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:5dc1818c77575d149e25fce9ef82dd7b7263ae372f03494158668ad632a69759", size = 8272736, upload-time = "2026-06-02T11:53:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/37/79/a733f02dc2118da7e77a134b34f39f40201a353311b011d20859d2db3556/scikit_learn-1.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:366652351f092b219c248f1e72821e841960a63d8f358f1dcfd54dc1cbdbbc28", size = 7919564, upload-time = "2026-06-02T11:53:21.2Z" }, + { url = "https://files.pythonhosted.org/packages/ac/20/75f915ff375d6249e6550ac740fdbbd66159a068fd3af1400ff62036b07a/scikit_learn-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2bd41b0d201bc81575531b96b713d3eb5e5f50fb0b82101ff0f92294fdc236ac", size = 8741122, upload-time = "2026-06-02T11:53:24.08Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d5/2b5148f2279196775e1db2aeb85d14b70ac80e7e32b3b28e7ebeafb0901d/scikit_learn-1.9.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5be45aa4a42a68a533913a6ed736cf309de2226411c79ef8d609a5456f1939b1", size = 8261512, upload-time = "2026-06-02T11:53:27.183Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ee/5adbc77656b71f9456a2f5a7a9fdb4bcf9207a6b962889f1c2f9323afa4e/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e50ed4da51974e86e940690e9a3d82e729b62b5a49f7c9bac534d515d39d86f", size = 8837603, upload-time = "2026-06-02T11:53:30.328Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c2/63fdda36c56437eeb44aaf9493c8bcd62ce230ab1598924fc626ffbfa943/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:056c92bb67ad4c28463c2f2653d9701449201e7e7a9e94e321be0f71c4fef2b8", size = 9132097, upload-time = "2026-06-02T11:53:33.456Z" }, + { url = "https://files.pythonhosted.org/packages/83/a4/c8e67227c680e2259c8864ae72ff48b06e16a6f51253a22167aa02a8aa4e/scikit_learn-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4306775fad04cc4b472a1b15af1ae9cede1540fbfcc17fbce3767cd8dc7ae283", size = 8211173, upload-time = "2026-06-02T11:53:36.602Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fd/3c0863792e98e67e9184aa4029288a175935eb65443afcd30d4f143450cf/scikit_learn-1.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:26e22435f63bcdcf396b574273f29f13dd531f5ea035801f5be10ba1540a4e60", size = 7867451, upload-time = "2026-06-02T11:53:39.075Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, +] +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/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, +] + +[[package]] +name = "scipy" +version = "1.18.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/19/ca10ead60b0acc80b2b833c2c4a4f2ff753d0f58b811f70d911c7e94a25c/scipy-1.18.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:7bd21faaf5a1a3b2eff922d02db5f191b99a6518db9078a8fb23169f6d22259a", size = 31056519, upload-time = "2026-06-19T14:59:45.203Z" }, + { url = "https://files.pythonhosted.org/packages/96/72/1e6442a00cd2924d361aa1b642ab6373ec35c6fabf311a760be9f76e0f13/scipy-1.18.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:265915e79107de9f946b855e50d7470d5893ec3f54b342e1aa6201cbdcd8bb6b", size = 28681889, upload-time = "2026-06-19T14:59:48.103Z" }, + { url = "https://files.pythonhosted.org/packages/9b/2d/11dd93d21e147a73ba22bd75c0b9208d3a2e0ec76d53170ce7d9029b1015/scipy-1.18.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9ab7b758be6940954a713ee466e2043e9f6e2ed965c1fce5c91039f4be3d90a9", size = 20423580, upload-time = "2026-06-19T14:59:50.665Z" }, + { url = "https://files.pythonhosted.org/packages/9c/01/93552f75e0d2a7dd115a45e59209c51e8d514daff02fc887d2623be06fe1/scipy-1.18.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:97b6cddaaee0a779ef6b5ca83c9604b27cc16b2b8fc22c142652df8793319fb8", size = 23054441, upload-time = "2026-06-19T14:59:53.564Z" }, + { url = "https://files.pythonhosted.org/packages/3c/23/21f5e703643d66f21faa6b4c73195bfcad70c55efcb4f1ab327cd7c4101a/scipy-1.18.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52a96e21517c7292375c0e27dd796a811f03fcea5fd4d108fdfea8145dcf17ab", size = 33968720, upload-time = "2026-06-19T14:59:56.415Z" }, + { url = "https://files.pythonhosted.org/packages/dd/aa/1b939f6c67ed68635bb538e6752d3dacc02f66535182e939a89581a44e9c/scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f55797419e16e7f30cf88ffb3113ce0467f00cfe3f70d5c281730b21769bfc2", size = 35287115, upload-time = "2026-06-19T14:59:59.411Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ff/eec46be7e9234208f801062b53e1983085eddebd693f6c9bfb03b459830d/scipy-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ad033410e2e0672ffdc1042110cef20e1c46f8fd0616cee1d44d8d58fad8fc11", size = 35577989, upload-time = "2026-06-19T15:00:02.235Z" }, + { url = "https://files.pythonhosted.org/packages/84/ca/210d4759c7210bb7d269437421959b39a33434e2776b60c5cb8a763bb30a/scipy-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a55985d54c769c872e64b7f4c8a81cc30ef700cc04296abbbf3705439c126de", size = 37421717, upload-time = "2026-06-19T15:00:05.102Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/9a9edb45345bd6744da5ddfb6628e5d5185920494c6a67ec45b6381004cb/scipy-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:71ccc8faa2dd16ac310233203474a8b5cb67f10dedd54a3116d34943f4b19132", size = 36597428, upload-time = "2026-06-19T15:00:08.112Z" }, + { url = "https://files.pythonhosted.org/packages/99/0e/33f32a2a58987e26aec0f7df252cbbad1e90ae77bdbc76f40dd4ed0cf0ea/scipy-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:d88363fd9d8fbd3511bd273f1a49efb2a540773ddf92a91d57498ce7dd7f3e76", size = 24351481, upload-time = "2026-06-19T15:00:11.103Z" }, +] + +[[package]] +name = "sentry-sdk" +version = "2.64.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/31/b7341f156a5f6f36f0b4845d6f1c28a2ae4799171dba7007f3a1e9b234b4/sentry_sdk-2.64.0.tar.gz", hash = "sha256:68be2c29e14ae310f8a39e1a79916b6d85c6cb41dcce789d14ff05fe293e4c55", size = 921020, upload-time = "2026-06-30T08:13:47.682Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/a8/3fb9a4319efa3b26f5be0e90e6d8918df43fa7c7e977d26390f589501d82/sentry_sdk-2.64.0-py3-none-any.whl", hash = "sha256:715ea91ca860a819e8d8a50a7bde3a80d0df3b4ed7b6660a20fb9a2d084188f1", size = 498901, upload-time = "2026-06-30T08:13:45.566Z" }, +] + +[[package]] +name = "setuptools" +version = "81.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "smmap" +version = "5.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, +] + +[[package]] +name = "sqlite-vec" +version = "0.1.9" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/85/9fad0045d8e7c8df3e0fa5a56c630e8e15ad6e5ca2e6106fceb666aa6638/sqlite_vec-0.1.9-py3-none-macosx_10_6_x86_64.whl", hash = "sha256:1b62a7f0a060d9475575d4e599bbf94a13d85af896bc1ce86ee80d1b5b48e5fb", size = 131171, upload-time = "2026-03-31T08:02:31.717Z" }, + { url = "https://files.pythonhosted.org/packages/a4/3d/3677e0cd2f92e5ebc43cd29fbf565b75582bff1ccfa0b8327c7508e1084f/sqlite_vec-0.1.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1d52e30513bae4cc9778ddbf6145610434081be4c3afe57cd877893bad9f6b6c", size = 165434, upload-time = "2026-03-31T08:02:32.712Z" }, + { url = "https://files.pythonhosted.org/packages/00/d4/f2b936d3bdc38eadcbd2a87875815db36430fab0363182ba5d12cd8e0b51/sqlite_vec-0.1.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e921e592f24a5f9a18f590b6ddd530eb637e2d474e3b1972f9bbeb773aa3cb9", size = 160076, upload-time = "2026-03-31T08:02:33.796Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ad/6afd073b0f817b3e03f9e37ad626ae341805891f23c74b5292818f49ac63/sqlite_vec-0.1.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux1_x86_64.whl", hash = "sha256:1515727990b49e79bcaf75fdee2ffc7d461f8b66905013231251f1c8938e7786", size = 163388, upload-time = "2026-03-31T08:02:34.888Z" }, + { url = "https://files.pythonhosted.org/packages/42/89/81b2907cda14e566b9bf215e2ad82fc9b349edf07d2010756ffdb902f328/sqlite_vec-0.1.9-py3-none-win_amd64.whl", hash = "sha256:4a28dc12fa4b53d7b1dced22da2488fade444e96b5d16fd2d698cd670675cf32", size = 292804, upload-time = "2026-03-31T08:02:36.035Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/1b/bc9e3e7a72dcdad7dc7888758f5d00f56f8909ed5cfdff822bd72bb4c520/sse_starlette-3.4.5.tar.gz", hash = "sha256:83072538bc211a2f68b7b0422226c4af3e9b62e106e07034664b832ca019842a", size = 35249, upload-time = "2026-06-20T17:36:58.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/75/c88d3f5dafd59c791da1ce27650d30bf5b70cbf1cbf01cd00e5f9e360915/sse_starlette-3.4.5-py3-none-any.whl", hash = "sha256:e71bad53323f65573c3864a6c3bd0c1eb6e5f092b2e48082b0c35927d19ca296", size = 16518, upload-time = "2026-06-20T17:36:56.729Z" }, +] + +[[package]] +name = "starlette" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, +] + +[[package]] +name = "structlog" +version = "25.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/52/9ba0f43b686e7f3ddfeaa78ac3af750292662284b3661e91ad5494f21dbc/structlog-25.5.0.tar.gz", hash = "sha256:098522a3bebed9153d4570c6d0288abf80a031dfdb2048d59a49e9dc2190fc98", size = 1460830, upload-time = "2025-10-27T08:28:23.028Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/45/a132b9074aa18e799b891b91ad72133c98d8042c70f6240e4c5f9dabee2f/structlog-25.5.0-py3-none-any.whl", hash = "sha256:a8453e9b9e636ec59bd9e79bbd4a72f025981b3ba0f5837aebf48f02f37a7f9f", size = 72510, upload-time = "2025-10-27T08:28:21.535Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tensorboard" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "grpcio" }, + { name = "markdown" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "setuptools" }, + { name = "tensorboard-data-server" }, + { name = "werkzeug" }, +] +wheels = [ + { 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]] +name = "tensorboard-data-server" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/13/e503968fefabd4c6b2650af21e110aa8466fe21432cd7c43a84577a89438/tensorboard_data_server-0.7.2-py3-none-any.whl", hash = "sha256:7e0610d205889588983836ec05dc098e80f97b7e7bbff7e994ebb78f578d0ddb", size = 2356, upload-time = "2023-10-23T21:23:32.16Z" }, + { url = "https://files.pythonhosted.org/packages/b7/85/dabeaf902892922777492e1d253bb7e1264cadce3cea932f7ff599e53fea/tensorboard_data_server-0.7.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:9fe5d24221b29625dbc7328b0436ca7fc1c23de4acf4d272f1180856e32f9f60", size = 4823598, upload-time = "2023-10-23T21:23:33.714Z" }, + { url = "https://files.pythonhosted.org/packages/73/c6/825dab04195756cf8ff2e12698f22513b3db2f64925bdd41671bfb33aaa5/tensorboard_data_server-0.7.2-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:ef687163c24185ae9754ed5650eb5bc4d84ff257aabdc33f0cc6f74d8ba54530", size = 6590363, upload-time = "2023-10-23T21:23:35.583Z" }, +] + +[[package]] +name = "tensorboardx" +version = "2.6.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/a9/fc520ea91ab1f3ba51cbf3fe24f2b6364ed3b49046969e0868d46d6da372/tensorboardx-2.6.5.tar.gz", hash = "sha256:ca176db3997ee8c07d2eb77381225956a3fd1c10c91beafab1f17069adc47017", size = 4770195, upload-time = "2026-04-03T15:40:23.803Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/0f/69fbab4c30b2f3a76e6de67585ea72a8eccf381751f5c0046b966fde9658/tensorboardx-2.6.5-py3-none-any.whl", hash = "sha256:c10b891d00af306537cb8b58a039b2ba41571f0da06f433a41c4ca8d6abe1373", size = 87510, upload-time = "2026-04-03T15:40:22.111Z" }, +] + +[[package]] +name = "termcolor" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" }, +] + +[[package]] +name = "terminaltexteffects" +version = "0.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/92/0eb3f0ad206bf449b7db75f061202dce27d8cb90e598ce3c7d32c0bd80b9/terminaltexteffects-0.12.2.tar.gz", hash = "sha256:4a5eef341d538743e7ac4341cd74d47afc9d0345acdad330ed03fd0a72e41f5f", size = 164321, upload-time = "2025-10-20T20:58:26.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/93/a588ab8b15ceeef23042aa52660fb4891a0e955e92cd3aa97dcafe621720/terminaltexteffects-0.12.2-py3-none-any.whl", hash = "sha256:4b986034094007aa9a31cb1bd16d5d8fcac9755fb6a5da8f74ee7b70c0fa2d63", size = 189344, upload-time = "2025-10-20T20:58:24.425Z" }, +] + +[[package]] +name = "textual" +version = "3.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", extra = ["linkify", "plugins"] }, + { name = "platformdirs" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/83/c99c252c3fad2f7010ceb476a31af042eec71da441ffeef75bb590bc2e9e/textual-3.7.1.tar.gz", hash = "sha256:a76ba0c8a6c194ef24fd5c3681ebfddca55e7127c064a014128c84fbd7f5d271", size = 1604038, upload-time = "2025-07-09T09:04:45.477Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/f1/8929fcce6dc983f7a260d0f3ddd2a69b74ba17383dbe57a7e0a9e085e8be/textual-3.7.1-py3-none-any.whl", hash = "sha256:ab5d153f4f65e77017977fa150d0376409e0acf5f1d2e25e2e4ab9de6c0d61ff", size = 691472, upload-time = "2025-07-09T09:04:43.626Z" }, +] + +[[package]] +name = "textual-serve" +version = "1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aiohttp-jinja2" }, + { name = "jinja2" }, + { name = "rich" }, + { name = "textual" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/7e/62fecc552853ec6a178cb1faa2d6f73b34d5512924770e7b08b58ff14148/textual_serve-1.1.3.tar.gz", hash = "sha256:f8f636ae2f5fd651b79d965473c3e9383d3521cdf896f9bc289709185da3f683", size = 448340, upload-time = "2025-11-01T16:22:36.723Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/fe/108e7773349d500cf363328c3d0b7123e03feda51e310a3a5b136ac8ca71/textual_serve-1.1.3-py3-none-any.whl", hash = "sha256:207a472bc6604e725b1adab4ab8bf12f4c4dc25b04eea31e4d04731d8bf30f18", size = 447339, upload-time = "2025-11-01T16:22:35.209Z" }, +] + +[[package]] +name = "thop" +version = "0.1.1.post2209072238" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/0f/72beeab4ff5221dc47127c80f8834b4bcd0cb36f6ba91c0b1d04a1233403/thop-0.1.1.post2209072238-py3-none-any.whl", hash = "sha256:01473c225231927d2ad718351f78ebf7cffe6af3bed464c4f1ba1ef0f7cdda27", size = 15443, upload-time = "2022-09-07T14:38:37.211Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "toolz" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613, upload-time = "2025-10-17T04:03:21.661Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093, upload-time = "2025-10-17T04:03:20.435Z" }, +] + +[[package]] +name = "torch" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/ed/ff0c4f8cef63977a646dc80e40c05cae873f4097b12dc87e1cd7e1cecf42/torch-2.12.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ec56e82be6a8b0c036771a77f7d32ad3c299770571af9815b3dafe61434389d5", size = 87967927, upload-time = "2026-06-17T21:08:43.16Z" }, + { url = "https://files.pythonhosted.org/packages/85/1b/c8ecf60c9dba535f9ea341c359c600c0bd877a7ca14b3296f13316321847/torch-2.12.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:42cd7339bf266f14944710e8274be63e7e012bb937834a8d85a8327a9860eba6", size = 426366829, upload-time = "2026-06-17T21:07:18.574Z" }, + { url = "https://files.pythonhosted.org/packages/ab/d6/73d4a3f27e00526e98086f3a64ab609af1345cca62367749fbc3c8e4b83c/torch-2.12.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a7817f0f89a796d9de239d06f69faf5d7e19a6a5db6710a5ead777c912f9f50a", size = 532144834, upload-time = "2026-06-17T21:08:00.633Z" }, + { url = "https://files.pythonhosted.org/packages/e3/51/4010c8fa6f9d1f42c054a321970ca95ec58e4e4494f5b53a34c3f3c9e310/torch-2.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:2af3d9cc866e0a15ae7635ff0a9c61d6624a353ad657f5bcd8d86c26cdc64693", size = 122949863, upload-time = "2026-06-17T21:08:39.016Z" }, + { url = "https://files.pythonhosted.org/packages/59/38/7028d3be540f1dcdf41660a2b01d0c51d2cb73915fe370d84e4d277a6d47/torch-2.12.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ef81f503912effea2ce3d9b12a2e3a6ed488943e91271c90c7a829f60baf6aa2", size = 87975425, upload-time = "2026-06-17T21:08:34.094Z" }, + { url = "https://files.pythonhosted.org/packages/5a/e3/750b3e3548635ceac03ba255daa26dbc7ed66ca3484dc4b4d955ab7f4501/torch-2.12.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:107df6888624bdea41508f9aeb6149d9333c737a5530ceecb56c904e811369ae", size = 426379894, upload-time = "2026-06-17T21:06:55.077Z" }, + { url = "https://files.pythonhosted.org/packages/dc/ca/ed24783da629ff3e640ba3f70a7639e9045d3d88b93ee6bc47b8a28a1f2c/torch-2.12.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:6e29e7e74d05bda7d955c75e99459f878ebd970ef851b4057edbd3b34a5eb4a3", size = 532169264, upload-time = "2026-06-17T21:08:17.65Z" }, + { url = "https://files.pythonhosted.org/packages/46/61/c63f0158446f3a98ea672b004d761b848911eba567ea4a624c7db5aadc04/torch-2.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:a513506cfda3c1c78dabeb6574c1597538c0254b3d39af174dde35d8177f4ce3", size = 122953086, upload-time = "2026-06-17T21:08:27.69Z" }, + { url = "https://files.pythonhosted.org/packages/f0/54/efb7ebca77970012b0cc21687a55d70eb2ba514b2c2b8e18d9fb1222f3be/torch-2.12.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:d2dd0f2c5f7ccbddaf34cade0deaf476808368f902b9cdb7f36a2ab42301bc0e", size = 87991951, upload-time = "2026-06-17T21:07:49.309Z" }, + { url = "https://files.pythonhosted.org/packages/1e/00/4210d76ca7424981f04033ebe7e48816ab83287a62538747a58825db770c/torch-2.12.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:2de4e19b88a481482c6c75291f2d6a52eda3ce51f311b29aa9b68499c830c07c", size = 426382721, upload-time = "2026-06-17T21:06:41.842Z" }, + { url = "https://files.pythonhosted.org/packages/76/1f/bc9f5a5aa569307076365f25afcebacb22e9c754b1bcfbaaa146627c7fda/torch-2.12.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:649e4ced014ba646f76f8cb9c9726735a6323eb321b7919f942790a923f90921", size = 532261322, upload-time = "2026-06-17T21:06:06.673Z" }, + { url = "https://files.pythonhosted.org/packages/9e/49/c549461daa008159d006a76a991fbc2f26fa8bac27a4030c858463dcb20f/torch-2.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:e86550597877fb272ddc52db2f85b82cb601ea7bd932576a0340152cae2200b3", size = 122988095, upload-time = "2026-06-17T21:07:44.9Z" }, +] + +[[package]] +name = "torchvision" +version = "0.27.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/10/8e3e5a70dded1f86368bc987d93fa0436e73a79060aead75a8783b040ebd/torchvision-0.27.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:68ba63b48af92f06db995adb23d8411993dba1dee705a4e92411b83a00930b7f", size = 1852109, upload-time = "2026-06-17T21:09:34.966Z" }, + { url = "https://files.pythonhosted.org/packages/4c/32/1a3eddb92e6d8ae69f38a20c60b7788c67c6ef32d6d4d1dc5d5fbd1f109c/torchvision-0.27.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:aabe47970a00a3c0574360bfbd4ca3d6162a51f3fa1283a29cd528018dd13088", size = 7829870, upload-time = "2026-06-17T21:09:18.695Z" }, + { url = "https://files.pythonhosted.org/packages/13/1c/6b45992279b4177d26b5b851f554ee5d99115e7bd02eeb99459ad41027bf/torchvision-0.27.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a9ea9a8abdd23466a5f1c09523cb27dc61e36c16fc7e5e88b337ca54f530b1ef", size = 7658441, upload-time = "2026-06-17T21:09:23.803Z" }, + { url = "https://files.pythonhosted.org/packages/7e/6b/09d2d6f04c3465a346202624a09cfcfb954f2b334d19de886a63056f86f9/torchvision-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:d0b00ae58379e6c936ce4816b1b8b94cefb1bd1c2a88eccbadfc993de7a430ca", size = 3493503, upload-time = "2026-06-17T21:09:28.426Z" }, + { url = "https://files.pythonhosted.org/packages/64/46/bc0ebd93282aeedc1759f054a252c6fadf14b42a0535db3233c85cce4ae5/torchvision-0.27.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ad8743a9c12c8c124ad0a1491e54c3ca0c749e91e374e3d92136060b22c9e0f4", size = 1852118, upload-time = "2026-06-17T21:09:32.448Z" }, + { url = "https://files.pythonhosted.org/packages/b2/00/752adc57b6aa8bb833f5b0672acb9538aa5535d64998b9d8dd48ee51fa80/torchvision-0.27.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a726707e4cbe438fcc507d787af7acf6bca52de30bf4b03579f1dfc0675da829", size = 7831256, upload-time = "2026-06-17T21:09:26.767Z" }, + { url = "https://files.pythonhosted.org/packages/fa/8a/c474fb27faba02e84dc40e0ac9ea1aa828d6d3557a378f7d0a22468bb2a3/torchvision-0.27.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a1d6a123009af59ad288459f579f67a65cbe8f59372dc7b97e41bc01a6a9b767", size = 7659995, upload-time = "2026-06-17T21:09:25.325Z" }, + { url = "https://files.pythonhosted.org/packages/eb/7c/e254f8e242a921adc2cc62c11674fa8a16d33e0a1b6c6f5436cb91628ee7/torchvision-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:f3b57a984283896f15c9698562418282f828332886c77315bf269936e6ba0280", size = 3807497, upload-time = "2026-06-17T21:09:31.234Z" }, + { url = "https://files.pythonhosted.org/packages/88/82/2e8fdc19e4f0bbe31d403a55d78318bcea4afcd3083e1e4700ef61ebb893/torchvision-0.27.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:448abfc3baba984da4577f737209e445da6be93e3b5f4799d90162bf61e3f485", size = 1852105, upload-time = "2026-06-17T21:09:33.695Z" }, + { url = "https://files.pythonhosted.org/packages/43/42/103fa8f9366cfd1329fe449d6b1a25a640c0c17862ed48f21c4af94af322/torchvision-0.27.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9edfb5a549fc2f30ccadb24eca907901e92e426c91a59316be6703a9360e5098", size = 7830902, upload-time = "2026-06-17T21:09:29.739Z" }, + { url = "https://files.pythonhosted.org/packages/97/70/fa6052a42110a3657fc94073648da6171220469f4bf9f27e6a0b9378075c/torchvision-0.27.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ae3d49e57c4abc8eafc1a1971f80fc4948a6268fa69340737ca4466936def080", size = 7664211, upload-time = "2026-06-17T21:09:17.206Z" }, + { url = "https://files.pythonhosted.org/packages/d0/95/27aca854da7e536a339f46bab1ef67823ac2ac97c59ab2b3203b373d46cf/torchvision-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:0b6e3aa98b7433506bbce1d0d05cb13ec787fc6eb8c5fbd998b26ce05f047543", size = 4079076, upload-time = "2026-06-17T21:09:15.907Z" }, +] + +[[package]] +name = "tqdm" +version = "4.68.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/d7/0535a28b1f5f24f6612fb3ff1e89fb1a8d160fee0f976e0aa6803862134b/tqdm-4.68.3.tar.gz", hash = "sha256:00dfa48452b6b6cfae3dd9885636c23d3422d1ec97c66d96818cbd5e0821d482", size = 170596, upload-time = "2026-06-17T07:36:52.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/8e/bb97bb0c71802080bfc8952937d174e49cfc50de5c951dd47b2496f0dcdb/tqdm-4.68.3-py3-none-any.whl", hash = "sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03", size = 78337, upload-time = "2026-06-17T07:36:50.132Z" }, +] + +[[package]] +name = "traitlets" +version = "5.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/a9/a2584b8313b89f94869ddb3c4074617a691de1812a614d2d50e32ca5a7a6/traitlets-5.15.1.tar.gz", hash = "sha256:7b1c07854fe25acb39e009bae49f11b79ff6cbb2f27999104e9110e7a6b53722", size = 163344, upload-time = "2026-06-03T12:26:06.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/8d/1080ee4c231f361b6ce4470d556c8c435b67c7e0753aaa641497ee92f88b/traitlets-5.15.1-py3-none-any.whl", hash = "sha256:770a53705f84b81ac107e83a1b3328ff2dae16094d8fc3cfc004e4b22dfd8e92", size = 85858, upload-time = "2026-06-03T12:26:04.395Z" }, +] + +[[package]] +name = "transformers" +version = "5.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/7c/8240f612819718100a9346dc28dea6a11370c3ca9c8c6eabadd3dea4ef29/transformers-5.12.1.tar.gz", hash = "sha256:679ee731c8225347889ad4fb3b2c926a62e9da3b7d284e9d12c791da7272466b", size = 8924054, upload-time = "2026-06-15T17:27:50.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/56/bbd60dd8668055803bf8ba55a81f9b8a8b31497f620109a9671d26a2076d/transformers-5.12.1-py3-none-any.whl", hash = "sha256:2a5e109d2021265df7098ffbb738295acaf5ad256f12cbc586db2ea4dcbb1a8a", size = 11150587, upload-time = "2026-06-15T17:27:46.679Z" }, +] + +[[package]] +name = "triton" +version = "3.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/ea/629cc37436ca5df93ce98956d09cd2ca1498bfee8ef4972d2fe48b9f958c/triton-3.7.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3daf64305d6cea88d3334c65ebc9bcd0c64c9564a977084366aa768d57cbcf64", size = 184551013, upload-time = "2026-06-17T20:03:37.551Z" }, + { url = "https://files.pythonhosted.org/packages/15/76/c79c34311625227a288df3e483fc5cdf3d596624cbd4b4758c4cbdc14af3/triton-3.7.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee89fbf782ec2ad50391dd1cf26cbea4f4467154c37f4773026da8fc31c0f58e", size = 197596267, upload-time = "2026-06-17T19:53:06.898Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f9/19d842d06a08559534fa1eaab6ca551b1bcf40f06620bddec1babaa2772d/triton-3.7.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4a0e1cd4c4a76370ed74a8432a53cea28716827d19e40ffc732233e35ceb3f6", size = 184664887, upload-time = "2026-06-17T20:03:42.913Z" }, + { url = "https://files.pythonhosted.org/packages/cd/5e/fce69606f7f240297f163e25539906732b199530d486ce67ae319877e821/triton-3.7.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6744957e9fd610a29680ec2346057d0c86948ed3812468670719f391e94b44a5", size = 197701306, upload-time = "2026-06-17T19:53:13.673Z" }, + { url = "https://files.pythonhosted.org/packages/94/fa/f856e24deb462d5f18bd4b5a746957862ab9b6ee5834bda60605ec348366/triton-3.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9497f2e696ee368862a181a90b2dcc03ca978cc4f602abd67c7d81022a6988e1", size = 184692359, upload-time = "2026-06-17T20:03:48.288Z" }, + { url = "https://files.pythonhosted.org/packages/c4/6f/fb96d15db6f36d6eae4cafb998c2e0353bf59d7c4ea1662d7497f269134a/triton-3.7.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e40869937a68206ec70d7f25bb7ec6433cb083f9135e1f36dbd318dc449a728", size = 197719725, upload-time = "2026-06-17T19:53:20.419Z" }, +] + +[[package]] +name = "typer" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "uc-micro-py" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/78/67/9a363818028526e2d4579334460df777115bdec1bb77c08f9db88f6389f2/uc_micro_py-2.0.0.tar.gz", hash = "sha256:c53691e495c8db60e16ffc4861a35469b0ba0821fe409a8a7a0a71864d33a811", size = 6611, upload-time = "2026-03-01T06:31:27.526Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/73/d21edf5b204d1467e06500080a50f79d49ef2b997c79123a536d4a17d97c/uc_micro_py-2.0.0-py3-none-any.whl", hash = "sha256:3603a3859af53e5a39bc7677713c78ea6589ff188d70f4fee165db88e22b242c", size = 6383, upload-time = "2026-03-01T06:31:26.257Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.49.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", marker = "python_full_version < '3.11' or sys_platform != 'emscripten'" }, + { name = "h11", marker = "python_full_version < '3.11' or sys_platform != 'emscripten'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/fa/e1388bbcf24ef3274f45c0c1c7b501fd14971037c1b6ee23610553307497/uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f", size = 71376, upload-time = "2026-06-03T22:01:29.037Z" }, +] + +[[package]] +name = "wandb" +version = "0.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "gitpython" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/a7/683bfbd6cbade3012bc90d3e9c4cfc72dd62566195bf4c30321946d64b77/wandb-0.28.0.tar.gz", hash = "sha256:b20e5af0fe80e2e2a466b0466a1d60cedcc578dce0f036eca04f4a0adcad95b6", size = 40558332, upload-time = "2026-06-23T00:38:50.115Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/47/1723605f76c5d6446b6d0db65b83eda1599721bc8c1e65bd76cc1682b1a7/wandb-0.28.0-py3-none-macosx_12_0_arm64.whl", hash = "sha256:c3dab1205a5aca4abbad1eca08902cdba86add0edfa83d8d61b4429d0e79fa87", size = 24335272, upload-time = "2026-06-23T00:38:26.002Z" }, + { url = "https://files.pythonhosted.org/packages/81/ff/42b539bc75bc48fc86981dccde89327ba9b71504b805b9ba42cba7c26de9/wandb-0.28.0-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:ae255da18726ee8e731ef82cbc85035b901a28ae14cf91604c361b44b8d44ce0", size = 25557959, upload-time = "2026-06-23T00:38:28.993Z" }, + { url = "https://files.pythonhosted.org/packages/15/55/c3db03d04aeab3726066a418b2ef6a1f8119774ee510f4fbe992f52b7472/wandb-0.28.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:6dbcba12ab168aa37561f2f32dcdef8713495fc25fa7d30fdc9bfb37989694dd", size = 24878557, upload-time = "2026-06-23T00:38:31.417Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5d/1385ce3c219cb5bd30d4027687e3f8d25969c7dfd09adad1cbd5080e1a72/wandb-0.28.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:325b2d0bd88be6eda5db10542499bad3710927f2569c81a84dc5eeaffc76825c", size = 26764727, upload-time = "2026-06-23T00:38:33.775Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/23b6c17a6d3d5422b007707961c4496b2f6f892624d2910c9f7742fcc202/wandb-0.28.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8954bc1c62ae43914dce2bebfd1d9957f72350f8fbb78e5cdfe2ca9b6be8a7b8", size = 25051656, upload-time = "2026-06-23T00:38:36.281Z" }, + { url = "https://files.pythonhosted.org/packages/89/67/9be00fb2db2281063af24a148636d2dd363d337317642ab5d8e93572c794/wandb-0.28.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9fec6c908554c2dad33110c1312bc3028cc2e430f0679f16b84f82c8ea801e3b", size = 27074113, upload-time = "2026-06-23T00:38:38.737Z" }, + { url = "https://files.pythonhosted.org/packages/59/b1/f7a96c09cab0c5131b1e6466659b093b401e1653cbe6bb77b462fc1c361d/wandb-0.28.0-py3-none-win32.whl", hash = "sha256:8834ef3a7c8c43b701654162783caa7ad37af48a0ff06fc35d0d65a411f76ccd", size = 24525206, upload-time = "2026-06-23T00:38:42.041Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c4/c7bed5e981679c74e9fbb22c03ff31c42e95f266199d03d8d325f4d0e6df/wandb-0.28.0-py3-none-win_amd64.whl", hash = "sha256:ac1f82292e2da4f98297b78c3a46726b3a6c5734ecb75fc39b8db2c8a4989159", size = 24525214, upload-time = "2026-06-23T00:38:44.549Z" }, + { url = "https://files.pythonhosted.org/packages/f0/77/b5ce9696c8cb955521a7941fbc443e78b2f504894c6ae1a2d0b1de6e12ae/wandb-0.28.0-py3-none-win_arm64.whl", hash = "sha256:c5b0faf1b84cf79ebabed77538c1940a4c6053e815f767a4004e877a1354bed1", size = 22378208, upload-time = "2026-06-23T00:38:47.148Z" }, +] + +[[package]] +name = "websocket-client" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, +] + +[[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/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343, upload-time = "2026-01-10T09:22:21.28Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021, upload-time = "2026-01-10T09:22:22.696Z" }, + { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320, upload-time = "2026-01-10T09:22:23.94Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815, upload-time = "2026-01-10T09:22:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054, upload-time = "2026-01-10T09:22:27.101Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565, upload-time = "2026-01-10T09:22:28.293Z" }, + { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848, upload-time = "2026-01-10T09:22:30.394Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249, upload-time = "2026-01-10T09:22:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685, upload-time = "2026-01-10T09:22:33.345Z" }, + { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, + { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, +] + +[[package]] +name = "winrt-runtime" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/dd/acdd527c1d890c8f852cc2af644aa6c160974e66631289420aa871b05e65/winrt_runtime-3.2.1.tar.gz", hash = "sha256:c8dca19e12b234ae6c3dadf1a4d0761b51e708457492c13beb666556958801ea", size = 21721, upload-time = "2025-06-06T14:40:27.593Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/28/26d86ca6d2f155f31ca61e069312034a8922a5a89f5d0fc68abb7c04aad1/winrt_runtime-3.2.1-cp310-cp310-win32.whl", hash = "sha256:25a2d1e2b45423742319f7e10fa8ca2e7063f01284b6e85e99d805c4b50bbfb3", size = 210993, upload-time = "2025-06-06T06:44:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/46/a4/f096687e0d1877d206bc5d1f5f07ff90e00b0772d69d4559ab2b6b37090b/winrt_runtime-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:dc81d5fb736bf1ddecf743928622253dce4d0aac9a57faad776d7a3834e13257", size = 242210, upload-time = "2025-06-06T06:44:02.366Z" }, + { url = "https://files.pythonhosted.org/packages/ff/81/46927ce4d79fc8f40f193f35204bce79eff7c496d888825a7a74d8560b6e/winrt_runtime-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:363f584b1e9fcb601e3e178636d8877e6f0537ac3c96ce4a96f06066f8ff0eae", size = 415833, upload-time = "2025-06-06T06:44:03.379Z" }, + { url = "https://files.pythonhosted.org/packages/90/8d/d7ae0e07cd85c7768de76e8578261854f2af72bd3a8a527bb675e8ae0eda/winrt_runtime-3.2.1-cp311-cp311-win32.whl", hash = "sha256:9e9b64f1ba631cc4b9fe60b8ff16fef3f32c7ce2fcc84735a63129ff8b15c022", size = 210798, upload-time = "2025-06-06T06:44:04.775Z" }, + { url = "https://files.pythonhosted.org/packages/ac/66/d05f6e6c0517654734e7f87fa1f0fbc965add9f27cc36b524d96331ab3d8/winrt_runtime-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0a9046ae416808420a358c51705af8ae100acd40bc578be57ddfdd51cbb0f9c", size = 242032, upload-time = "2025-06-06T06:44:06.103Z" }, + { url = "https://files.pythonhosted.org/packages/39/a5/760c8396110f6d3e4c417752da1a2bf3b89e0998329c2f10afc717ef6291/winrt_runtime-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:e94f3cb40ea2d723c44c82c16d715c03c6b3bd977d135b49535fdd5415fd9130", size = 415659, upload-time = "2025-06-06T06:44:07.007Z" }, + { url = "https://files.pythonhosted.org/packages/d3/54/3dd06f2341fab6abb06588a16b30e0b213b0125be7b79dafc3bdba3b334a/winrt_runtime-3.2.1-cp312-cp312-win32.whl", hash = "sha256:762b3d972a2f7037f7db3acbaf379dd6d8f6cda505f71f66c6b425d1a1eae2f1", size = 210090, upload-time = "2025-06-06T06:44:08.151Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a1/1d7248d5c62ccbea5f3e0da64ca4529ce99c639c3be2485b6ed709f5c740/winrt_runtime-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:06510db215d4f0dc45c00fbb1251c6544e91742a0ad928011db33b30677e1576", size = 241391, upload-time = "2025-06-06T06:44:09.442Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ae/6a205d8dafc79f7c242be7f940b1e0c1971fd64ab3079bda4b514aa3d714/winrt_runtime-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:14562c29a087ccad38e379e585fef333e5c94166c807bdde67b508a6261aa195", size = 415242, upload-time = "2025-06-06T06:44:10.407Z" }, +] + +[[package]] +name = "winrt-windows-devices-bluetooth" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/a0/1c8a0c469abba7112265c6cb52f0090d08a67c103639aee71fc690e614b8/winrt_windows_devices_bluetooth-3.2.1.tar.gz", hash = "sha256:db496d2d92742006d5a052468fc355bf7bb49e795341d695c374746113d74505", size = 23732, upload-time = "2025-06-06T14:41:20.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/b7/822da8bc0b6a67cc0c3e460fef793f00c51a6fe59aa54f6bfe416519a9d9/winrt_windows_devices_bluetooth-3.2.1-cp310-cp310-win32.whl", hash = "sha256:49489351037094a088a08fbdf0f99c94e3299b574edb211f717c4c727770af78", size = 105569, upload-time = "2025-06-06T07:00:05.406Z" }, + { url = "https://files.pythonhosted.org/packages/68/46/696893d3bae80751e35fb0fb8fae5e7fc94a5354dfb5e19167d415e27c66/winrt_windows_devices_bluetooth-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:20f6a21029034c18ea6a6b6df399671813b071102a0d6d8355bb78cf4f547cdb", size = 114743, upload-time = "2025-06-06T07:00:06.408Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6a/a36b28739b73cc2c67050da866b063af135b5f6c071997c85a27adb6815c/winrt_windows_devices_bluetooth-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:69c523814eab795bc1bf913292309cb1025ef0a67d5fc33863a98788995e551d", size = 105021, upload-time = "2025-06-06T07:00:07.299Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cf/671bf29337323cc08f9969cb32312f217d2927d29dbf2964f0dbb378cb90/winrt_windows_devices_bluetooth-3.2.1-cp311-cp311-win32.whl", hash = "sha256:f4082a00b834c1e34b961e0612f3e581356bdb38c5798bd6842f88ec02e5152b", size = 105535, upload-time = "2025-06-06T07:00:08.146Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d5/5761a8b6dcc56957018970dd443059c8ee8a79de7b07f0b4d143f8e7dc15/winrt_windows_devices_bluetooth-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:44277a3f2cc5ac32ce9b4b2d96c5c5f601d394ac5f02cc71bcd551f738660e2d", size = 114612, upload-time = "2025-06-06T07:00:08.984Z" }, + { url = "https://files.pythonhosted.org/packages/24/0b/7819bb102286752d3572a75d03e6a8000ffe3c6cb7aee3eb136dca383fe2/winrt_windows_devices_bluetooth-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:0803a417403a7d225316b9b0c4fe3f8446579d6a22f2f729a2c21f4befc74a80", size = 105017, upload-time = "2025-06-06T07:00:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/54/ff/c4a3de909a875b46fad5e9f4fd412bba48571405bfa802b878954abf128c/winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win32.whl", hash = "sha256:18c833ec49e7076127463679e85efc59f61785ade0dc185c852586b21be1f31c", size = 105752, upload-time = "2025-06-06T07:00:10.684Z" }, + { url = "https://files.pythonhosted.org/packages/e7/78/bfee1f0c8d188c561c5b946ab21f6a0037e60dea110e80b1d6a1d529639f/winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:9b6702c462b216c91e32388023a74d0f87210cef6fd5d93b7191e9427ce2faca", size = 113356, upload-time = "2025-06-06T07:00:11.541Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1b/d9da9c29d36cabadef4e19c3e9ba6d2692f6a28224c81fcff757132ea0da/winrt_windows_devices_bluetooth-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:419fd1078c7749119f6b4bbf6be4e586e03a0ed544c03b83178f1d85f1b3d148", size = 104724, upload-time = "2025-06-06T07:00:12.406Z" }, +] + +[[package]] +name = "winrt-windows-devices-bluetooth-advertisement" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/fc/7ffe66ca4109b9e994b27c00f3d2d506e6e549e268791f755287ad9106d8/winrt_windows_devices_bluetooth_advertisement-3.2.1.tar.gz", hash = "sha256:0223852a7b7fa5c8dea3c6a93473bd783df4439b1ed938d9871f947933e574cc", size = 16906, upload-time = "2025-06-06T14:41:21.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/b9/c2b0d201b8b38895809591d089a5edc37e702a23f3a6bc6e542c5e7d6dbf/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp310-cp310-win32.whl", hash = "sha256:a758c5f81a98cc38347fdfb024ce62720969480e8c5b98e402b89d2b09b32866", size = 89730, upload-time = "2025-06-06T07:00:18.451Z" }, + { url = "https://files.pythonhosted.org/packages/56/f9/f086c3ac17745a71d8384e1831cab0d5a7c737e1fe5cb84d7584f6c14bbf/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:f982ef72e729ddd60cdb975293866e84bb838798828933012a57ee4bf12b0ea1", size = 95825, upload-time = "2025-06-06T07:00:19.385Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b5/f7f830b2da1fb7ffcaf25ce2734db0019615111f8f39e7b4d83fea4a0bd0/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:e88a72e1e09c7ccc899a9e6d2ab3fc0f43b5dd4509bcc49ec4abf65b55ab015f", size = 89402, upload-time = "2025-06-06T07:00:20.178Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5e/c628719e877a89f00cac7ce53f9666acbc5ed6f074130729d5d6768b63ff/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp311-cp311-win32.whl", hash = "sha256:fe17c2cf63284646622e8b2742b064bf7970bbf53cfab02062136c67fa6b06c9", size = 89614, upload-time = "2025-06-06T07:00:20.952Z" }, + { url = "https://files.pythonhosted.org/packages/ac/1a/d172d6f1c2fae53535e7f23835025cf39e3002749a0304f18a38e8ed490d/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:78e99dd48b4d89b71b7778c5085fdba64e754dd3ebc54fd09c200fe5222c6e09", size = 95783, upload-time = "2025-06-06T07:00:21.764Z" }, + { url = "https://files.pythonhosted.org/packages/67/c1/568dfdaea62ca3b13bb70162cb292e5cd0be5bbb98b738961ddcc2edd374/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6d5d2295474deab444fc4311580c725a2ca8a814b0f3344d0779828891d75401", size = 89253, upload-time = "2025-06-06T07:00:22.603Z" }, + { url = "https://files.pythonhosted.org/packages/c9/15/ad05c28e049208c97011728e2debdb45439175f75efe357b6faa4c9ba099/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win32.whl", hash = "sha256:901933cc40de5eb7e5f4188897c899dd0b0f577cb2c13eab1a63c7dfe89b08c4", size = 90033, upload-time = "2025-06-06T07:00:23.421Z" }, + { url = "https://files.pythonhosted.org/packages/26/48/074779081841f6eba4987930c4e7adcec38a5985b7dffd9fecc41f39a89c/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:e6c66e7d4f4ca86d2c801d30efd2b9673247b59a2b4c365d9e11650303d68d89", size = 95824, upload-time = "2025-06-06T07:00:24.238Z" }, + { url = "https://files.pythonhosted.org/packages/aa/25/e01966033a02b2d0718710bb47ef4f6b9b5a619ca2c857e06eb5c8e3ed13/winrt_windows_devices_bluetooth_advertisement-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:447d19defd8982d39944642eb7ebe89e4e20259ec9734116cf88879fb2c514ff", size = 89311, upload-time = "2025-06-06T07:00:25.029Z" }, +] + +[[package]] +name = "winrt-windows-devices-bluetooth-genericattributeprofile" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/21/aeeddc0eccdfbd25e543360b5cc093233e2eab3cdfb53ad3cabae1b5d04d/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1.tar.gz", hash = "sha256:cdf6ddc375e9150d040aca67f5a17c41ceaf13a63f3668f96608bc1d045dde71", size = 38896, upload-time = "2025-06-06T14:41:22.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/a3/449ffc2f8e4c3cfbe7f14c1b43bcaa0475fbd2e8e8bf08465399c5ea078c/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp310-cp310-win32.whl", hash = "sha256:af4914d7b30b49232092cd3b934e3ed6f5d3b1715ba47238541408ee595b7f46", size = 182059, upload-time = "2025-06-06T07:00:47.095Z" }, + { url = "https://files.pythonhosted.org/packages/50/d9/6ea88731df569f5c1b086daf4c3496c8d43281588e3a578ea623fef6bc43/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:0e557dd52fc80392b8bd7c237e1153a50a164b3983838b4ac674551072efc9ed", size = 187866, upload-time = "2025-06-06T07:00:48.123Z" }, + { url = "https://files.pythonhosted.org/packages/e9/2c/ace56fd32ad07608462de0ac7df218e0bf810e4cc31f2c0fbd7f5f90ee93/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:64cff62baa6b7aadd6c206e61d149113fdcda17360feb6e9d05bc8bbda4b9fde", size = 184627, upload-time = "2025-06-06T07:00:49.087Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5e/349a5d958be8c0570f0a49bbb746088bcfaa81555accb57503ba01185359/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp311-cp311-win32.whl", hash = "sha256:832cf65d035a11e6dbfef4fd66abdcc46be7e911ec96e2e72e98e12d8d5b9d3c", size = 182312, upload-time = "2025-06-06T07:00:49.974Z" }, + { url = "https://files.pythonhosted.org/packages/90/db/929ab0085ec89e46bd3a58c74b451dd770c3285dfa0cbd4f4aa4730da004/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:8179638a6c721b0bbf04ba251ef98d5e02d9a17f0cce377398e42c4fbb441415", size = 187768, upload-time = "2025-06-06T07:00:50.853Z" }, + { url = "https://files.pythonhosted.org/packages/a3/53/f316e2224c384178204430439f04f9b72017fe8237e341a9aebb20da8191/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:70b7edfca3190b89ae38bf60972b11978311b6d933d3142ae45560c955dbf5c7", size = 184189, upload-time = "2025-06-06T07:00:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/9c/a1/75ac783a5faee9b455fef2f53b7fef97b21ed60d52401b44c690202141e4/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win32.whl", hash = "sha256:ef894d21e0a805f3e114940254636a8045335fa9de766c7022af5d127dfad557", size = 183326, upload-time = "2025-06-06T07:00:52.662Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d9/a9dcc15322d2f5c7dfd491bd7ab121e36437caf78ebfa92bc0dd0546e2ca/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:db05de95cd1b24a51abb69cb936a8b17e9214e015757d0b37e3a5e207ddceb3d", size = 187810, upload-time = "2025-06-06T07:00:53.594Z" }, + { url = "https://files.pythonhosted.org/packages/d2/fc/47d00af076f558267097af3050910beda6bf8a21ceaa5830bbd26fcaf85e/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d4e131cf3d15fc5ad81c1bcde3509ac171298217381abed6bdf687f29871984", size = 184516, upload-time = "2025-06-06T07:00:55.24Z" }, +] + +[[package]] +name = "winrt-windows-devices-enumeration" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/dd/75835bfbd063dffa152109727dedbd80f6e92ea284855f7855d48cdf31c9/winrt_windows_devices_enumeration-3.2.1.tar.gz", hash = "sha256:df316899e39bfc0ffc1f3cb0f5ee54d04e1d167fbbcc1484d2d5121449a935cf", size = 23538, upload-time = "2025-06-06T14:41:26.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/61/2744d0e0b3fa7807149a1a36dd89abba901d6b24184d9fd5ef3f28467232/winrt_windows_devices_enumeration-3.2.1-cp310-cp310-win32.whl", hash = "sha256:40dac777d8f45b41449f3ff1ae70f0d457f1ede53f53962a6e2521b651533db5", size = 130040, upload-time = "2025-06-06T07:01:56.337Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f9/881b7ee8acdf3c9fe6c79d8ccd90f9246b397fc78420d55014c4ac05b822/winrt_windows_devices_enumeration-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:a101ec3e0ad0a0783032fdcd5dc48e7cd68ee034cbde4f903a8c7b391532c71a", size = 142463, upload-time = "2025-06-06T07:01:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/12/db/b09dffcf1158b35d81d8d57bf19ad04293870cea5afa77943c87f1110d88/winrt_windows_devices_enumeration-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:3296a3863ac086928ff3f3dc872b2a2fb971dab728817424264f3ca547504e9e", size = 135871, upload-time = "2025-06-06T07:01:58.792Z" }, + { url = "https://files.pythonhosted.org/packages/a6/92/ca1fd311d96fce15fba25543a2ae3cb829744a8af548a11d74233d0e4f64/winrt_windows_devices_enumeration-3.2.1-cp311-cp311-win32.whl", hash = "sha256:9f29465a6c6b0456e4330d4ad09eccdd53a17e1e97695c2e57db0d4666cc0011", size = 129898, upload-time = "2025-06-06T07:01:59.687Z" }, + { url = "https://files.pythonhosted.org/packages/03/fd/5bd5da5d7997725ba3f1995c16aa1c3362937f8ff68ad4cadfd3415eebcb/winrt_windows_devices_enumeration-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2a725d04b4cb43aa0e2af035f73a60d16a6c0ff165fcb6b763383e4e33a975fd", size = 142361, upload-time = "2025-06-06T07:02:00.546Z" }, + { url = "https://files.pythonhosted.org/packages/df/be/d423b63e740600e0617ddb85fba3ef99e7bbff02299fe46323bfe624a382/winrt_windows_devices_enumeration-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6365ef5978d4add26678827286034acf474b6b133aa4054e76567d12194e6817", size = 135808, upload-time = "2025-06-06T07:02:01.4Z" }, + { url = "https://files.pythonhosted.org/packages/31/3e/81642208ecd6c6c936f35a39a433c54e3f68e09d316546b8f953581ae334/winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win32.whl", hash = "sha256:1db22b0292b93b0688d11ad932ad1f3629d4f471310281a2fbfe187530c2c1f3", size = 130249, upload-time = "2025-06-06T07:02:02.237Z" }, + { url = "https://files.pythonhosted.org/packages/00/f4/a9ede5f3f0d86abfc7590726cf711133d97419b49ced372fca532e4f0696/winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:a73bc88d7f510af454f2b392985501c96f39b89fd987140708ccaec1588ceebc", size = 141512, upload-time = "2025-06-06T07:02:03.424Z" }, + { url = "https://files.pythonhosted.org/packages/31/ef/4fad07c03124bdc3acd64f80f3bd3cc4417ea641e07bb16a9503afd3e554/winrt_windows_devices_enumeration-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:2853d687803f0dd76ae1afe3648abc0453e09dff0e7eddbb84b792eddb0473ca", size = 135383, upload-time = "2025-06-06T07:02:04.312Z" }, +] + +[[package]] +name = "winrt-windows-devices-radios" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/02/9704ea359ad8b0d6faa1011f98fb477e8fb6eac5201f39d19e73c2407e7b/winrt_windows_devices_radios-3.2.1.tar.gz", hash = "sha256:4dc9b9d1501846049eb79428d64ec698d6476c27a357999b78a8331072e18a0b", size = 5908, upload-time = "2025-06-06T14:41:44.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/1b/0de659ed4bb80aee28753b4431011334205637a2578481a511866a11e0cf/winrt_windows_devices_radios-3.2.1-cp310-cp310-win32.whl", hash = "sha256:f97766fd551d06c102155d51b2922f96663dee045e1f8d57177def0a2149cb78", size = 38643, upload-time = "2025-06-06T07:07:56.852Z" }, + { url = "https://files.pythonhosted.org/packages/29/fd/67c6db8a3244ecc95f85970a7b0e749cda28e26563db1274c3db36a8fbe4/winrt_windows_devices_radios-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:104b737fa1279a3b6a88ba3c6236157afc1de03c472657c45e5176ad7a209e23", size = 40295, upload-time = "2025-06-06T07:07:57.738Z" }, + { url = "https://files.pythonhosted.org/packages/15/6d/d145c7f90b01c24f4f9885d1f7d430ecaf2a2b42b6bc236701791b0b0a06/winrt_windows_devices_radios-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:55b02877d2de06ca6f0f6140611a9af9d0c65710e28f1afdeaac1040433b1837", size = 37060, upload-time = "2025-06-06T07:07:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a0/4a8b51da15de218cec04bcc1cd85b4b93bcfd8ebe50a5f0a7eee28836dc6/winrt_windows_devices_radios-3.2.1-cp311-cp311-win32.whl", hash = "sha256:7c02790472414b6cda00d24a8cd23bca18e4b7474ddad4f9264f4484b891807e", size = 38505, upload-time = "2025-06-06T07:07:59.204Z" }, + { url = "https://files.pythonhosted.org/packages/de/49/ba69e3180585dbc6f3336a09fef7cba4558a6a1e7d500500f62c1478418e/winrt_windows_devices_radios-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:f87745486d313ba1e7562ca97f25ad436ec01ad4b3b9ea349fb6b6f25cb41104", size = 40157, upload-time = "2025-06-06T07:07:59.948Z" }, + { url = "https://files.pythonhosted.org/packages/9c/92/64817f71a20ecf842da36dc3848f42614217688137a69c93fda8a6103155/winrt_windows_devices_radios-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:6cee6f946ff3a3571850d1ca745edaee7c331d06ca321873e650779654effc4a", size = 36976, upload-time = "2025-06-06T07:08:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/a5/e0/4731a3c412318b2c5e74a8803a32e2fb9afc2c98368c6b61a422eb359e7e/winrt_windows_devices_radios-3.2.1-cp312-cp312-win32.whl", hash = "sha256:c3e683ce682338a5a5ed465f735e223ba7a22f16d0bbea2d070962bc7657edbb", size = 38606, upload-time = "2025-06-06T07:08:01.477Z" }, + { url = "https://files.pythonhosted.org/packages/37/8e/91464854dfc9e0be9ce8dcbe2bd6a67c19b68ab91584fc5de0f4f13e78f8/winrt_windows_devices_radios-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:a116e552a3f38607b9be558fb2e7de9b4450d1f9080069944d74d80cdda1873e", size = 40172, upload-time = "2025-06-06T07:08:02.214Z" }, + { url = "https://files.pythonhosted.org/packages/c3/0d/1bd62f606b6c4dfa936fccc4712be5506a40fc5d1b7177c3d3cbcaf30972/winrt_windows_devices_radios-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:4c28822f9251c9d547324f596b5c2581f050254ded05e5b786c650a3502744c1", size = 36989, upload-time = "2025-06-06T07:08:03.295Z" }, +] + +[[package]] +name = "winrt-windows-foundation" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/55/098ce7ea0679efcc1298b269c48768f010b6c68f90c588f654ec874c8a74/winrt_windows_foundation-3.2.1.tar.gz", hash = "sha256:ad2f1fcaa6c34672df45527d7c533731fdf65b67c4638c2b4aca949f6eec0656", size = 30485, upload-time = "2025-06-06T14:41:53.344Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/69/d387332c4378b41f87211b7dc40a4cfc6b7047dc227448aaa207624fc911/winrt_windows_foundation-3.2.1-cp310-cp310-win32.whl", hash = "sha256:677e98165dcbbf7a2367f905bc61090ef2c568b6e465f87cf7276df4734f3b0b", size = 111969, upload-time = "2025-06-06T07:10:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/52/71/046c1e2424627c3db66d764871186de4d26936e8a138d6bf04dc143e4606/winrt_windows_foundation-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:a8f27b4f0fdb73ccc4a3e24bc8010a6607b2bdd722fa799eafce7daa87d19d39", size = 118695, upload-time = "2025-06-06T07:10:56.782Z" }, + { url = "https://files.pythonhosted.org/packages/e0/2e/2463bc4ad984836fb3ecf1abac62df67bc5cabab004cad09b828b86ed51b/winrt_windows_foundation-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:d900c6165fab4ea589811efa2feed27b532e1b6f505f63bf63e2052b8cb6bdc4", size = 109690, upload-time = "2025-06-06T07:10:57.618Z" }, + { url = "https://files.pythonhosted.org/packages/c0/36/09b9757f7cbf269e67008ea2ad188a44f974c94c9b49ebf0b52d1a8c4069/winrt_windows_foundation-3.2.1-cp311-cp311-win32.whl", hash = "sha256:d1b5970241ccd61428f7330d099be75f4f52f25e510d82c84dbbdaadd625e437", size = 111944, upload-time = "2025-06-06T07:10:58.496Z" }, + { url = "https://files.pythonhosted.org/packages/05/a5/216d66df6bdcee58eb3877fabc1544337e23f850bf9f93838db7f5698371/winrt_windows_foundation-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:f3762be2f6e0f2aedf83a0742fd727290b397ffe3463d963d29211e4ebb53a7e", size = 118465, upload-time = "2025-06-06T07:10:59.678Z" }, + { url = "https://files.pythonhosted.org/packages/be/ca/48ca8b5bc5be5c7a5516c9e1d9a21861b4217e1b4ee57923aab6f13fa411/winrt_windows_foundation-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:806c77818217b3476e6c617293b3d5b0ff8a9901549dc3417586f6799938d671", size = 109609, upload-time = "2025-06-06T07:11:00.54Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f8/495e304ddedd5ff2f196efbde906265cb75ade4d79e2937837f72ef654a0/winrt_windows_foundation-3.2.1-cp312-cp312-win32.whl", hash = "sha256:867642ccf629611733db482c4288e17b7919f743a5873450efb6d69ae09fdc2b", size = 112169, upload-time = "2025-06-06T07:11:01.438Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5e/b5059e4ece095351c496c9499783130c302d25e353c18031d5231b1b3b3c/winrt_windows_foundation-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:45550c5b6c2125cde495c409633e6b1ea5aa1677724e3b95eb8140bfccbe30c9", size = 118668, upload-time = "2025-06-06T07:11:02.475Z" }, + { url = "https://files.pythonhosted.org/packages/a5/70/acbcb3ef07b1b67e2de4afab9176a5282cfd775afd073efe6828dfc65ace/winrt_windows_foundation-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:94f4661d71cb35ebc52be7af112f2eeabdfa02cb05e0243bf9d6bd2cafaa6f37", size = 109671, upload-time = "2025-06-06T07:11:03.538Z" }, +] + +[[package]] +name = "winrt-windows-foundation-collections" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/62/d21e3f1eeb8d47077887bbf0c3882c49277a84d8f98f7c12bda64d498a07/winrt_windows_foundation_collections-3.2.1.tar.gz", hash = "sha256:0eff1ad0d8d763ad17e9e7bbd0c26a62b27215016393c05b09b046d6503ae6d5", size = 16043, upload-time = "2025-06-06T14:41:53.983Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/26/ed3d35ea262999d28be957c35a32e93360eac0ef9f14e75d32cd6b5c6a37/winrt_windows_foundation_collections-3.2.1-cp310-cp310-win32.whl", hash = "sha256:46948484addfc4db981dab35688d4457533ceb54d4954922af41503fddaa8389", size = 59880, upload-time = "2025-06-06T07:11:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/cb/39/b4a1aeba2d13c1f2ad3d851d5092b8397c05f34fb318d6a7d499f5b5720b/winrt_windows_foundation_collections-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:899eaa3a93c35bfb1857d649e8dd60c38b978dda7cedd9725fcdbcebba156fd6", size = 70650, upload-time = "2025-06-06T07:11:11.396Z" }, + { url = "https://files.pythonhosted.org/packages/9f/74/f8a4a29202da24f2af2c4a8f515b0a44fe46bc4d25b3d54ea2249e980bd3/winrt_windows_foundation_collections-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:c36eb49ad1eba1b32134df768bb47af13cabb9b59f974a3cea37843e2d80e0e6", size = 59216, upload-time = "2025-06-06T07:11:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/87/b3/7e4a75c62e86bedf9458b7ec8dfed74cff3236e0b4b2288f95967d5cc4d2/winrt_windows_foundation_collections-3.2.1-cp311-cp311-win32.whl", hash = "sha256:9b272d9936e7db4840881c5dcf921eb26789ae4ef23fb6ec15e13e19a16254e7", size = 59693, upload-time = "2025-06-06T07:11:13.388Z" }, + { url = "https://files.pythonhosted.org/packages/32/58/049db1d95fdfc0c8451dc6db17442ed4e6b2aba361c425c0bb8dc8c98c4a/winrt_windows_foundation_collections-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:c646a5d442dd6540ade50890081ca118b41f073356e19032d0a5d7d0d38fbc89", size = 70828, upload-time = "2025-06-06T07:11:14.54Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6b/a04974f5555c86452e54c19d063d9fd45f0fe9f2a6858e7fe12c639043fb/winrt_windows_foundation_collections-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:2c4630027c93cdd518b0cf4cc726b8fbdbc3388e36d02aa1de190a0fc18ca523", size = 59051, upload-time = "2025-06-06T07:11:15.379Z" }, + { url = "https://files.pythonhosted.org/packages/1d/0b/7802349391466d3f7e8f62f588f36a1a0b6560abfcdbdaa426fe21d322b4/winrt_windows_foundation_collections-3.2.1-cp312-cp312-win32.whl", hash = "sha256:15704eef3125788f846f269cf54a3d89656fa09a1dc8428b70871f717d595ad6", size = 60060, upload-time = "2025-06-06T07:11:16.173Z" }, + { url = "https://files.pythonhosted.org/packages/37/94/5b888713e472746635a382e523513ab1b8200af55c5b56bc70e1e4369115/winrt_windows_foundation_collections-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:550dfb8c82fe74d9e0728a2a16a9175cc9e34ca2b8ef758d69b2a398894b698b", size = 69058, upload-time = "2025-06-06T07:11:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3c/829273622c9b37c67b97f187b92be318404f7d33db045e31d72b7d50f54c/winrt_windows_foundation_collections-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:810ad4bd11ab4a74fdbcd3ed33b597ef7c0b03af73fc9d7986c22bcf3bd24f84", size = 58793, upload-time = "2025-06-06T07:11:17.837Z" }, +] + +[[package]] +name = "winrt-windows-storage-streams" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "winrt-runtime", marker = "python_full_version < '3.11' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/50/f4488b07281566e3850fcae1021f0285c9653992f60a915e15567047db63/winrt_windows_storage_streams-3.2.1.tar.gz", hash = "sha256:476f522722751eb0b571bc7802d85a82a3cae8b1cce66061e6e758f525e7b80f", size = 34335, upload-time = "2025-06-06T14:43:23.905Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/4d/a0d806f4664b9bcf525bd31dcdf1f9520cc14f033e897dc7f7dd4ad4eb77/winrt_windows_storage_streams-3.2.1-cp310-cp310-win32.whl", hash = "sha256:89bb2d667ebed6861af36ed2710757456e12921ee56347946540320dacf6c003", size = 127791, upload-time = "2025-06-06T14:01:56.192Z" }, + { url = "https://files.pythonhosted.org/packages/99/2c/00baa87041a3d92a3cc5230d4033e995a52740e9c08fcd9f7bde93cb979f/winrt_windows_storage_streams-3.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:48a78e5dc7d3488eb77e449c278bc6d6ac28abcdda7df298462c4112d7635d00", size = 132608, upload-time = "2025-06-06T14:01:57.3Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d0/ed03e864aa8eaaec964d5bbc95baccf738275ae6cc88600db66ecb5adaf4/winrt_windows_storage_streams-3.2.1-cp310-cp310-win_arm64.whl", hash = "sha256:da71231d4a554f9f15f1249b4990c6431176f6dfb0e3385c7caa7896f4ca24d6", size = 128495, upload-time = "2025-06-06T14:01:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/a9e0dc03434aa29e6b5c83067e988cd5934adf830cd9f87cbbc06569ca32/winrt_windows_storage_streams-3.2.1-cp311-cp311-win32.whl", hash = "sha256:7dace2f9e364422255d0e2f335f741bfe7abb1f4d4f6003622b2450b87c91e69", size = 127509, upload-time = "2025-06-06T14:01:58.971Z" }, + { url = "https://files.pythonhosted.org/packages/23/98/6c9c21b5e75ff5927a130da9eaf5ab628dfa1f93b64c181f0193706cbd6c/winrt_windows_storage_streams-3.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:b02fa251a7eef6081eca1a5f64ecf349cfd1ac0ac0c5a5a30be52897d060bed5", size = 132491, upload-time = "2025-06-06T14:01:59.788Z" }, + { url = "https://files.pythonhosted.org/packages/38/ca/d0a02045d445cbf1029d65f01b487fdded5b333c0367a8bae0565b3def00/winrt_windows_storage_streams-3.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:efdf250140340a75647e8e8ad002782d91308e9fdd1e19470a5b9cc969ae4780", size = 128577, upload-time = "2025-06-06T14:02:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/e7/7d3f2a4a442f264e05cab2bdf20ed1b95cb3f753bd1b0f277f2b49fb8335/winrt_windows_storage_streams-3.2.1-cp312-cp312-win32.whl", hash = "sha256:77c1f0e004b84347b5bd705e8f0fc63be8cd29a6093be13f1d0869d0d97b7d78", size = 127787, upload-time = "2025-06-06T14:02:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2f/cc36f475f8af293f40e2c2a5d6c2e75a189c2c2d4d01ecb3551578518c79/winrt_windows_storage_streams-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4508ee135af53e4fc142876abbf4bc7c2a95edfc7d19f52b291a8499cacd6dc", size = 131849, upload-time = "2025-06-06T14:02:03.09Z" }, + { url = "https://files.pythonhosted.org/packages/94/84/896fb734f7456910ec412f3f3adfdc3f0dc3134864a496d5b120592f3bfd/winrt_windows_storage_streams-3.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:040cb94e6fb26b0d00a00e8b88b06fadf29dfe18cf24ed6cb3e69709c3613307", size = 128144, upload-time = "2025-06-06T14:02:03.946Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/df/f1c7a3de0831cd83194f1a85c5bb431b13f81e6b45079314c86d1c4ef3f2/yarl-1.24.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12", size = 129057, upload-time = "2026-05-19T21:27:47.564Z" }, + { url = "https://files.pythonhosted.org/packages/48/41/7daafb32dd7562bf45b1ce56562e7e1a9146f6479b6456873eb8a3413c40/yarl-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0", size = 91545, upload-time = "2026-05-19T21:27:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/a8/8f/7b3ec212f1ea0683f55f978e3246bc313c38818664edfc97a9f349a4901e/yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75", size = 91380, upload-time = "2026-05-19T21:27:51.953Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1b/8bafab7db23b0567ae9db749099b329d91e3b82bc6028b2050ba583e116c/yarl-1.24.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727", size = 105957, upload-time = "2026-05-19T21:27:53.98Z" }, + { url = "https://files.pythonhosted.org/packages/7f/77/21030c2f8d21d21559719beafc772ada2014be933418ed1eaed9cc800e42/yarl-1.24.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413", size = 97242, upload-time = "2026-05-19T21:27:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/50/d8/f9ea63d1b6aa910a866e089d871fff6cbd49caab29b86b35221a62dfa0d5/yarl-1.24.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9", size = 114719, upload-time = "2026-05-19T21:27:58.037Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a3/04e0ee98ac58a249ea7ed75223f5f901ba81a834f0b4921b58e5cec11757/yarl-1.24.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2", size = 112140, upload-time = "2026-05-19T21:27:59.618Z" }, + { url = "https://files.pythonhosted.org/packages/02/ad/0b9cc9f38a7324a7eb1d80f834eaa5283d17e9271bbda3186e598dddaeac/yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90", size = 106721, upload-time = "2026-05-19T21:28:02.586Z" }, + { url = "https://files.pythonhosted.org/packages/65/e7/a52478ebfc66ec989e085c6ae038b9f1bfa4190baa193b133b669c709e2f/yarl-1.24.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643", size = 106478, upload-time = "2026-05-19T21:28:04.523Z" }, + { url = "https://files.pythonhosted.org/packages/04/d8/5508530fea8472542de00013ae280765fc938ee196fc4030c43a498afb36/yarl-1.24.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac", size = 105423, upload-time = "2026-05-19T21:28:06.515Z" }, + { url = "https://files.pythonhosted.org/packages/84/f1/ece28505e9628e8b756e11bb4f28864a17cc33b6b44db4d2aaf0622bf630/yarl-1.24.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f", size = 99878, upload-time = "2026-05-19T21:28:08.637Z" }, + { url = "https://files.pythonhosted.org/packages/3f/52/fb5d34529b46dd84013afcfb30b8d2bc2832ed03d412736f577d604fa393/yarl-1.24.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36", size = 114025, upload-time = "2026-05-19T21:28:10.64Z" }, + { url = "https://files.pythonhosted.org/packages/43/f0/ff9d31aaab024f7a251c0ed308a98ae29bf9f7dc344e78f28b1322431ca2/yarl-1.24.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a", size = 105613, upload-time = "2026-05-19T21:28:12.784Z" }, + { url = "https://files.pythonhosted.org/packages/31/7d/3296fb3f3ecd52bf9ae6c16b0895c1cda7e9170a2083861552b683f70264/yarl-1.24.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53", size = 111665, upload-time = "2026-05-19T21:28:14.393Z" }, + { url = "https://files.pythonhosted.org/packages/1a/74/77aa6ddaca4fbf42e45e675a465c43956dd40702281049975a2aa04eae59/yarl-1.24.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342", size = 106914, upload-time = "2026-05-19T21:28:15.893Z" }, + { url = "https://files.pythonhosted.org/packages/d8/02/7611f22cd1d4ed7373eb7f9ee21fde1046edba2e7c0e514880d760352f48/yarl-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4", size = 92658, upload-time = "2026-05-19T21:28:17.471Z" }, + { url = "https://files.pythonhosted.org/packages/91/00/671d0add79938127292839ae44506ce2f7fe8909c72d5a931864f128fd0b/yarl-1.24.2-cp310-cp310-win_arm64.whl", hash = "sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39", size = 87887, upload-time = "2026-05-19T21:28:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971, upload-time = "2026-05-19T21:28:20.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507, upload-time = "2026-05-19T21:28:22.556Z" }, + { url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343, upload-time = "2026-05-19T21:28:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704, upload-time = "2026-05-19T21:28:25.872Z" }, + { url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281, upload-time = "2026-05-19T21:28:27.352Z" }, + { url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020, upload-time = "2026-05-19T21:28:29.458Z" }, + { url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450, upload-time = "2026-05-19T21:28:31.262Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384, upload-time = "2026-05-19T21:28:32.856Z" }, + { url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153, upload-time = "2026-05-19T21:28:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322, upload-time = "2026-05-19T21:28:36.642Z" }, + { url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057, upload-time = "2026-05-19T21:28:38.386Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502, upload-time = "2026-05-19T21:28:40.091Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253, upload-time = "2026-05-19T21:28:41.987Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345, upload-time = "2026-05-19T21:28:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558, upload-time = "2026-05-19T21:28:45.806Z" }, + { url = "https://files.pythonhosted.org/packages/21/3c/f960d7a65ef97d8ba9b424fb5128796a4bc710fc6df2ddbbd7dfdc3bbd20/yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45", size = 92808, upload-time = "2026-05-19T21:28:48.465Z" }, + { url = "https://files.pythonhosted.org/packages/03/1a/49fb03750e4de4d2284cd5b885a383133c34eef45bd59631b2bb8b7e81e8/yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122", size = 87610, upload-time = "2026-05-19T21:28:50.07Z" }, + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901, upload-time = "2026-05-19T21:29:20.014Z" }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229, upload-time = "2026-05-19T21:29:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, +] + +[[package]] +name = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +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/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, +] diff --git a/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/__init__.py b/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/__init__.py index 87f6f7e393..8c3c7133c6 100644 --- a/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/__init__.py +++ b/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/__init__.py @@ -15,6 +15,7 @@ ObservationKind, ProtocolVersion, RobotMotorSurface, + RuntimeActionFrame, RuntimeDescription, ScoreOutput, StepRequest, @@ -38,6 +39,7 @@ "ObservationKind", "ProtocolVersion", "RobotMotorSurface", + "RuntimeActionFrame", "RuntimeDescription", "ScoreOutput", "StepRequest", diff --git a/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/compat.py b/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/compat.py index 4131854bc9..16a275c8c2 100644 --- a/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/compat.py +++ b/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/compat.py @@ -17,7 +17,6 @@ from __future__ import annotations from dataclasses import dataclass - from itertools import zip_longest from dimos_runtime_protocol.models import ProtocolVersion, RuntimeDescription diff --git a/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/models.py b/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/models.py index bf52643291..c899df3953 100644 --- a/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/models.py +++ b/packages/dimos-runtime-protocol/src/dimos_runtime_protocol/models.py @@ -28,7 +28,16 @@ def __str__(self) -> str: from typing import Literal -from pydantic import BaseModel, ConfigDict, Field, NonNegativeFloat, NonNegativeInt, PositiveInt +from pydantic import ( + BaseModel, + ConfigDict, + Field, + FiniteFloat, + NonNegativeFloat, + NonNegativeInt, + PositiveInt, + model_validator, +) from dimos_runtime_protocol.types import JsonObject, JsonValue from dimos_runtime_protocol.version import PROTOCOL_VERSION @@ -143,6 +152,22 @@ class MotorActionFrame(StrictModel): sequence: NonNegativeInt = 0 +class RuntimeActionFrame(StrictModel): + """Semantic runtime action frame for non-motor action surfaces.""" + + frame_type: Literal["runtime_action"] + space_id: str + values: list[FiniteFloat] + sequence: NonNegativeInt | None = None + tick_id: NonNegativeInt | None = None + + @model_validator(mode="after") + def _has_sequence_or_tick(self) -> RuntimeActionFrame: + if self.sequence is None and self.tick_id is None: + raise ValueError("RuntimeActionFrame requires sequence or tick_id") + return self + + class MotorStateFrame(StrictModel): """Ordered motor state frame returned by a sidecar.""" @@ -173,7 +198,7 @@ class StepRequest(StrictModel): episode_id: str tick_id: NonNegativeInt - action: MotorActionFrame + action: MotorActionFrame | RuntimeActionFrame class StepResponse(StrictModel): diff --git a/packages/dimos-runtime-protocol/tests/test_models.py b/packages/dimos-runtime-protocol/tests/test_models.py index 9307c47a7b..8c91b236c9 100644 --- a/packages/dimos-runtime-protocol/tests/test_models.py +++ b/packages/dimos-runtime-protocol/tests/test_models.py @@ -20,6 +20,7 @@ ObservationKind, ProtocolVersion, RobotMotorSurface, + RuntimeActionFrame, RuntimeDescription, StepRequest, check_compatible, @@ -79,6 +80,113 @@ def test_action_extra_fields_rejected() -> None: ) +def test_step_request_accepts_motor_action_frame_payload() -> None: + request = StepRequest.model_validate( + { + "episode_id": "ep", + "tick_id": 1, + "action": { + "robot_id": "panda", + "mode": "position", + "names": ["panda/joint1"], + "q": [0.0], + "sequence": 7, + }, + } + ) + + assert isinstance(request.action, MotorActionFrame) + assert request.action.mode == CommandMode.POSITION + assert request.action.sequence == 7 + + +def test_runtime_action_frame_round_trip() -> None: + frame = RuntimeActionFrame( + frame_type="runtime_action", + space_id="libero.ee_delta_6d_gripper.normalized.v1", + values=[0.0, 0.1, -0.2, 0.3, -0.4, 0.5, 1.0], + tick_id=2, + ) + + decoded = from_json_bytes(to_json_bytes(frame), RuntimeActionFrame) + + assert decoded == frame + assert decoded.frame_type == "runtime_action" + + +def test_step_request_accepts_runtime_action_frame_payload() -> None: + request = StepRequest.model_validate( + { + "episode_id": "ep", + "tick_id": 2, + "action": { + "frame_type": "runtime_action", + "space_id": "libero.ee_delta_6d_gripper.normalized.v1", + "values": [0.0, 0.1, -0.2, 0.3, -0.4, 0.5, 1.0], + "sequence": 10, + }, + } + ) + + assert isinstance(request.action, RuntimeActionFrame) + assert request.action.space_id == "libero.ee_delta_6d_gripper.normalized.v1" + assert request.action.sequence == 10 + + +@pytest.mark.parametrize("value", [float("inf"), float("-inf"), float("nan")]) +def test_runtime_action_frame_rejects_non_finite_values(value: float) -> None: + with pytest.raises(ValidationError): + RuntimeActionFrame.model_validate( + { + "frame_type": "runtime_action", + "space_id": "libero.ee_delta_6d_gripper.normalized.v1", + "values": [0.0, value], + "sequence": 1, + } + ) + + +def test_runtime_action_frame_requires_sequence_or_tick_id() -> None: + with pytest.raises(ValidationError): + RuntimeActionFrame.model_validate( + { + "frame_type": "runtime_action", + "space_id": "libero.ee_delta_6d_gripper.normalized.v1", + "values": [0.0], + } + ) + + +@pytest.mark.parametrize( + "action_payload", + [ + { + "frame_type": "runtime_action", + "space_id": "libero.ee_delta_6d_gripper.normalized.v1", + "values": [0.0], + "sequence": 1, + "robot_id": "panda", + }, + { + "frame_type": "unsupported", + "space_id": "libero.ee_delta_6d_gripper.normalized.v1", + "values": [0.0], + "sequence": 1, + }, + { + "space_id": "libero.ee_delta_6d_gripper.normalized.v1", + "values": [0.0], + "sequence": 1, + }, + ], +) +def test_step_request_rejects_ambiguous_or_unsupported_actions( + action_payload: dict[str, object], +) -> None: + with pytest.raises(ValidationError): + StepRequest.model_validate({"episode_id": "ep", "tick_id": 1, "action": action_payload}) + + def test_incompatible_protocol_version_rejected() -> None: result = check_compatible(ProtocolVersion(version="1.0", min_compatible="1.0")) diff --git a/pyproject.toml b/pyproject.toml index 9e99bfd46e..df6df0d965 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,6 +139,9 @@ dependencies = [ # DimSim scene client (dimos/simulation/dimsim/scene_client.py) imports `websocket` # at module load; DimSim is a non-extra-gated robot connection backend. "websocket-client>=1.8", + # Policy rollout video artifacts. + "imageio>=2.37.0", + "imageio-ffmpeg>=0.6.0", ] @@ -634,6 +637,7 @@ ignore = [ "packages/dimos-gpd-grasp-demo/pixi.lock", "packages/dimos-gpd-grasp-demo/uv.lock", "packages/dimos-robosuite-sidecar/uv.lock", + "packages/dimos-libero-pro-sidecar/uv.lock", "packages/dimos-gpd-worker-demo/pixi.lock", "packages/dimos-gpd-worker-demo/uv.lock", "*/package-lock.json", diff --git a/scripts/benchmarks/demo_lerobot_libero_policy_rollout.py b/scripts/benchmarks/demo_lerobot_libero_policy_rollout.py new file mode 100644 index 0000000000..d8898226aa --- /dev/null +++ b/scripts/benchmarks/demo_lerobot_libero_policy_rollout.py @@ -0,0 +1,1066 @@ +#!/usr/bin/env python3 +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Run LeRobot LIBERO policy rollout gates through DimOS seams. + +The default run evaluates the official `lerobot/VLA-JEPA-LIBERO` checkpoint over +the 50-episode LIBERO object matrix: task indices 0..9 crossed with init states +0..4. The pass condition is strict: success_rate > 0.50. + +Pass `--policy-family xvla --episodes-limit 10` to run the X-VLA LIBERO gate +configuration with `lerobot/xvla-libero`; run once without `--live-policy-stream` +for the synchronous benchmark stage, then again with `--live-policy-stream` for +the module-native live stage. + +For a lightweight native-action smoke test that does not download LeRobot, pass +`--fake-backend --episodes-limit 1 --no-enforce-gate`. +""" + +from __future__ import annotations + +import argparse +from collections.abc import Mapping, Sequence +from dataclasses import asdict, is_dataclass +import json +from pathlib import Path +import sys +import time +import traceback +from typing import Protocol +from uuid import uuid4 + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROTOCOL_SRC = REPO_ROOT / "packages" / "dimos-runtime-protocol" / "src" +LIBERO_PRO_SIDECAR_SRC = REPO_ROOT / "packages" / "dimos-libero-pro-sidecar" / "src" + +for package_src in (PROTOCOL_SRC, LIBERO_PRO_SIDECAR_SRC): + sys.path.insert(0, str(package_src)) + +from dimos_libero_pro_sidecar.blueprint import libero_pro_runtime_blueprint +from dimos_libero_pro_sidecar.module import LiberoProRuntimeModule +from dimos_runtime_protocol import ( + EpisodeResetRequest, + EpisodeResetResponse, + ObservationFrame, + RuntimeActionFrame, + StepRequest, + StepResponse, +) +import imageio.v2 as imageio +import numpy as np + +from dimos.benchmark.runtime.artifacts import write_json +from dimos.control.components import HardwareComponent, HardwareType +from dimos.control.coordinator import ControlCoordinator, TaskConfig +from dimos.core.coordination.blueprints import autoconnect +from dimos.core.coordination.module_coordinator import ModuleCoordinator +from dimos.robot_learning.policy_rollout.backends.backend import PolicyBackend +from dimos.robot_learning.policy_rollout.evaluation import ( + BenchmarkEpisodeResult, + BenchmarkEpisodeSpec, + BenchmarkEvaluationSummary, + BenchmarkPolicyEvalModule, + LiberoRobotPolicyObservationBuilder, + PolicyEvalRuntimeSession, + RuntimeStreamSnapshot, + libero_object_episode_matrix, +) +from dimos.robot_learning.policy_rollout.models import ( + BackendBatch, + BackendOutputEnvelope, + PolicyBackendDescription, + RobotPolicyObservation, +) +from dimos.robot_learning.policy_rollout.robot_policy_module import RobotPolicyModule +from dimos.robot_learning.policy_rollout.vla_jepa_libero_contract import ( + VLA_JEPA_LIBERO_ACTION_SPACE_ID, +) +from dimos.robot_learning.policy_rollout.xvla_libero_contract import ( + XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID, +) +from dimos.simulation.runtime_client.shm_motor import MotorShmOwner + +DEFAULT_CAMERAS = ("agentview", "robot0_eye_in_hand") +DEFAULT_ARTIFACT_DIR = REPO_ROOT / "artifacts" / "benchmark" / "lerobot-vla-jepa-libero" +DEFAULT_POLICY_FAMILY = "vla_jepa" +POLICY_FAMILY_DEFAULTS: Mapping[str, Mapping[str, object]] = { + "vla_jepa": { + "checkpoint": "lerobot/VLA-JEPA-LIBERO", + "contract_type": "vla_jepa_libero", + "action_space_id": VLA_JEPA_LIBERO_ACTION_SPACE_ID, + "runtime_action_mode": "native", + "expected_benchmark_episodes": 50, + "expected_live_episodes": 10, + }, + "xvla": { + "checkpoint": "lerobot/xvla-libero", + "contract_type": "xvla_libero", + "action_space_id": XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID, + "runtime_action_mode": "native_absolute", + "expected_benchmark_episodes": 10, + "expected_live_episodes": 10, + "documented_control_mode": "absolute", + }, +} + + +class DescribedPolicyModule(Protocol): + def describe_backend(self) -> PolicyBackendDescription: ... + + def close(self) -> None: ... + + +class _LiberoProRuntime(Protocol): + def reset(self, request: EpisodeResetRequest) -> EpisodeResetResponse: ... + + def step(self, request: StepRequest) -> StepResponse: ... + + def observation_snapshot(self) -> tuple[list[ObservationFrame], dict[str, np.ndarray]]: ... + + def stop(self) -> None: ... + + +class FixedActionBackend: + """PolicyBackend test double that exercises contract conversion without LeRobot.""" + + def __init__(self, action: Sequence[float], *, use_action_chunk: bool = False) -> None: + if len(action) != 7: + raise ValueError("fixed action must have exactly 7 values") + self._action = tuple(float(value) for value in action) + self._use_action_chunk = use_action_chunk + self._initialized = False + self._episode_resets = 0 + + def initialize(self) -> None: + self._initialized = True + + def reset_episode(self) -> None: + self._episode_resets += 1 + + def infer_batch(self, batch: BackendBatch) -> BackendOutputEnvelope: + if not self._initialized: + raise RuntimeError("FixedActionBackend was not initialized") + output: tuple[float, ...] | tuple[tuple[float, ...], ...] = self._action + if self._use_action_chunk: + output = (self._action,) + return BackendOutputEnvelope( + output=output, + metadata={ + "backend_type": "fixed_action", + "batch_metadata": dict(batch.metadata), + "episode_resets": self._episode_resets, + "use_action_chunk": self._use_action_chunk, + }, + ) + + def close(self) -> None: + self._initialized = False + + def describe(self) -> PolicyBackendDescription: + return PolicyBackendDescription( + backend_type="fixed_action", + checkpoint_id=None, + supports_episode_reset=True, + metadata={"action": list(self._action), "episode_resets": self._episode_resets}, + ) + + +class ModuleLiberoRuntimeSession(PolicyEvalRuntimeSession): + """Policy-evaluation runtime session backed by the placed LIBERO runtime module.""" + + def __init__( + self, + runtime: _LiberoProRuntime, + coordinator: ModuleCoordinator, + expected_image_streams: Sequence[str], + stream_timeout_s: float = 5.0, + ) -> None: + self._runtime = runtime + self._coordinator = coordinator + self._expected_image_streams = tuple(expected_image_streams) + self._stream_timeout_s = stream_timeout_s + self._last_snapshot = RuntimeStreamSnapshot() + + def reset(self, request: EpisodeResetRequest) -> EpisodeResetResponse: + response = self._runtime.reset(request) + self._last_snapshot = self._take_snapshot() + return response + + def step(self, request: StepRequest) -> StepResponse: + response = self._runtime.step(request) + self._last_snapshot = self._take_snapshot() + return response + + def latest_observation_snapshot(self) -> RuntimeStreamSnapshot: + return self._last_snapshot + + def close(self) -> None: + self._coordinator.stop() + + def _take_snapshot(self) -> RuntimeStreamSnapshot: + observations, stream_values = self._runtime.observation_snapshot() + missing = [stream for stream in self._expected_image_streams if stream not in stream_values] + if missing: + raise RuntimeError( + f"runtime module did not publish expected image streams {missing} " + f"within {self._stream_timeout_s:.1f}s" + ) + return RuntimeStreamSnapshot(observations=tuple(observations), values=stream_values) + + +class _LivePolicyCoordinator: + """Module-native live policy path with stream/RPC policy-to-control wiring.""" + + def __init__(self, *, args: argparse.Namespace) -> None: + self._args = args + self._action_names = [f"policy_action/{index}" for index in range(7)] + self._owner = MotorShmOwner(f"policy_action_{uuid4().hex}", self._action_names) + self._component = HardwareComponent( + hardware_id="policy_action", + hardware_type=HardwareType.WHOLE_BODY, + joints=self._action_names, + adapter_type="benchmark_runtime", + address=self._owner.key, + auto_enable=False, + adapter_kwargs={ + "motor_names": self._action_names, + "connect_timeout_s": 5.0, + }, + ) + self._task_name = "policy_chunk_action" + self._module_coordinator: ModuleCoordinator | None = None + self._policy_module: RobotPolicyModule | None = None + self._control_coordinator: ControlCoordinator | None = None + self._policy_observation_transport: object | None = None + self._last_command_sequence = 0 + self.chunk_count = 0 + self.consumed_actions = 0 + self.stale_deactivations = 0 + + @property + def last_command_sequence(self) -> int: + return self._last_command_sequence + + def start(self) -> None: + policy_blueprint = self._policy_blueprint() + control_blueprint = ControlCoordinator.blueprint( + tick_rate=float(self._args.control_step_hz), + publish_joint_state=False, + hardware=[self._component], + tasks=[ + TaskConfig( + name=self._task_name, + type="policy_chunk", + joint_names=self._action_names, + auto_start=True, + params={ + "accepted_action_space_id": _action_space_id(self._args), + "ticks_per_action": 1, + "execute_first_n": 1, + "stale_timeout_ticks": 1, + "action_mapping": "target", + "policy_trigger_method": "trigger_policy_action_chunk_inference", + }, + ) + ], + ) + blueprint = ( + autoconnect(policy_blueprint, control_blueprint) + .remappings( + [ + ( + RobotPolicyModule, + "policy_action_chunk", + "robot_policy_action_chunk", + ) + ] + ) + .global_config(viewer="none", n_workers=1) + ) + self._module_coordinator = ModuleCoordinator.build(blueprint) + self._policy_module = self._module_coordinator.get_instance(RobotPolicyModule) + self._control_coordinator = self._module_coordinator.get_instance(ControlCoordinator) + if self._policy_module is None or self._control_coordinator is None: + raise RuntimeError( + "module-native live policy blueprint did not deploy required modules" + ) + self._policy_observation_transport = self._module_coordinator._transport_registry[ + ("policy_observation", RobotPolicyObservation) + ] + + def _policy_blueprint(self): # type: ignore[no-untyped-def] + if self._args.fake_backend: + return RobotPolicyModule.blueprint( + backend_type="fixed_action", + backend_params={ + "action": _fixed_action_values(self._args.fixed_action), + "use_action_chunk": True, + }, + contract_type=_contract_type(self._args), + contract_params={}, + ) + return RobotPolicyModule.blueprint( + backend_type="lerobot", + backend_params={ + "policy_family": _policy_family(self._args), + "checkpoint_id": _checkpoint(self._args), + "device": self._args.device, + "use_action_chunk": True, + }, + contract_type=_contract_type(self._args), + contract_params={}, + ) + + def reset_episode(self, episode_id: str) -> None: + if self._policy_module is None: + raise RuntimeError("live policy modules have not been started") + self._policy_module.reset(episode_id=episode_id) + + def update_observation(self, sample: RobotPolicyObservation) -> None: + if self._policy_observation_transport is None: + raise RuntimeError("live policy modules have not been started") + publish = getattr(self._policy_observation_transport, "publish", None) + if not callable(publish): + raise RuntimeError("policy observation transport does not support publish") + publish(sample) + + def describe_backend(self) -> PolicyBackendDescription: + if self._policy_module is None: + policy_module = _policy_module(self._args) + try: + return policy_module.describe_backend() + finally: + policy_module.close() + return self._policy_module.describe_backend() + + def diagnostics(self) -> dict[str, object]: + if self._control_coordinator is None: + return {} + diagnostics = self._control_coordinator.task_invoke(self._task_name, "diagnostics") + if not isinstance(diagnostics, dict): + return {} + return {str(key): value for key, value in diagnostics.items()} + + def next_action_values(self, *, timeout_s: float) -> tuple[float, ...] | None: + deadline = time.monotonic() + timeout_s + while time.monotonic() < deadline: + sequence, commands = self._owner.read_commands() + if sequence > self._last_command_sequence: + self._last_command_sequence = sequence + self.consumed_actions += 1 + return tuple(float(command.q) for command in commands) + time.sleep(0.001) + self.stale_deactivations += 1 + return None + + def close(self) -> None: + if self._module_coordinator is not None: + self._module_coordinator.stop() + self._module_coordinator = None + self._policy_module = None + self._control_coordinator = None + self._policy_observation_transport = None + self._owner.close() + self._owner.unlink() + + +def run_policy_gate(args: argparse.Namespace) -> BenchmarkEvaluationSummary: + artifact_dir = _repo_path(args.artifact_dir) + artifact_dir.mkdir(parents=True, exist_ok=True) + episodes = _selected_episodes(args.episodes_limit) + policy_module = None if args.live_policy_stream else _policy_module(args) + live: _LivePolicyCoordinator | None = None + results: list[BenchmarkEpisodeResult] = [] + cleanup: dict[str, object] = {"episodes": []} + try: + if args.live_policy_stream: + live = _LivePolicyCoordinator(args=args) + live.start() + for episode in episodes: + episode_dir = artifact_dir / "episodes" / episode.episode_id + if live is not None: + result = _run_one_live_episode(args, episode, live, episode_dir, cleanup) + else: + if policy_module is None: + raise RuntimeError("synchronous policy module was not initialized") + result = _run_one_episode(args, episode, policy_module, episode_dir, cleanup) + results.append(result) + summary = _summary(results, args.success_threshold) + _write_aggregate_artifacts( + artifact_dir, + results, + summary, + live if live is not None else policy_module, + args, + cleanup, + ) + if args.enforce_gate and not summary.passed: + raise SystemExit( + f"LeRobot LIBERO policy gate failed: " + f"success_rate={summary.success_rate:.3f} <= {summary.success_threshold:.3f}" + ) + return summary + except Exception as exc: + _write_setup_failure_artifacts(artifact_dir, live or policy_module, args, cleanup, exc) + raise + finally: + if live is not None: + live.close() + if policy_module is not None: + policy_module.close() + + +def _run_one_episode( + args: argparse.Namespace, + episode: BenchmarkEpisodeSpec, + policy_module: RobotPolicyModule, + episode_dir: Path, + cleanup: dict[str, object], +) -> BenchmarkEpisodeResult: + client = _start_runtime(args, episode) + cleanup_entry: dict[str, object] = { + "episode_id": episode.episode_id, + "runtime": "module-native", + } + cast_cleanup = cleanup.setdefault("episodes", []) + if isinstance(cast_cleanup, list): + cast_cleanup.append(cleanup_entry) + try: + eval_module = BenchmarkPolicyEvalModule( + runtime_session=client, + robot_policy_module=policy_module, + artifact_dir=str(episode_dir), + max_steps=args.max_steps, + success_threshold=args.success_threshold, + close_policy_on_finish=False, + video_dir=str(episode_dir / "videos") if args.save_videos else None, + video_streams=args.camera_names if args.save_videos else (), + video_fps=args.control_step_hz, + ) + try: + eval_module.run_episodes([episode]) + if not eval_module.last_results: + raise RuntimeError(f"episode {episode.episode_id} produced no result") + return eval_module.last_results[0] + finally: + eval_module.close() + finally: + _stop_runtime(client, episode_dir, cleanup_entry) + + +def _run_one_live_episode( + args: argparse.Namespace, + episode: BenchmarkEpisodeSpec, + live: _LivePolicyCoordinator, + episode_dir: Path, + cleanup: dict[str, object], +) -> BenchmarkEpisodeResult: + client = _start_runtime(args, episode) + cleanup_entry: dict[str, object] = { + "episode_id": episode.episode_id, + "runtime": "module-native-live-policy-stream", + } + cast_cleanup = cleanup.setdefault("episodes", []) + if isinstance(cast_cleanup, list): + cast_cleanup.append(cleanup_entry) + diagnostics: dict[str, object] = {} + try: + result, diagnostics = _run_live_episode_loop( + args, + episode, + client, + live, + episode_dir, + ) + episode_dir.mkdir(parents=True, exist_ok=True) + write_json(episode_dir / "live_path_diagnostics.json", _json_ready(diagnostics)) + return result + finally: + cleanup_entry["live_path_diagnostics"] = diagnostics + _stop_runtime(client, episode_dir, cleanup_entry) + + +def _run_live_episode_loop( + args: argparse.Namespace, + episode: BenchmarkEpisodeSpec, + client: PolicyEvalRuntimeSession, + live: _LivePolicyCoordinator, + episode_dir: Path, +) -> tuple[BenchmarkEpisodeResult, dict[str, object]]: + reset_response = client.reset( + EpisodeResetRequest( + episode_id=episode.episode_id, + task_id=episode.task_id, + seed=episode.seed, + options={ + **dict(episode.options), + "task_index": episode.task_index, + "init_state_index": episode.init_state_index, + }, + ) + ) + live.reset_episode(episode.episode_id) + runtime_description = reset_response.runtime_description + episode_dir.mkdir(parents=True, exist_ok=True) + write_json(episode_dir / "runtime_description.json", _json_ready(runtime_description)) + sample_builder = LiberoRobotPolicyObservationBuilder() + snapshot = client.latest_observation_snapshot() + observations = snapshot.observations + reward_sum = 0.0 + done = False + success: bool | None = None + last_values: tuple[float, ...] = () + observed_streams = snapshot.observed_streams + video_streams = tuple(args.camera_names) if args.save_videos else () + video_frames: dict[str, list[np.ndarray]] = {stream: [] for stream in video_streams} + steps = 0 + stale_waits = 0 + consecutive_stale_waits = 0 + + for tick_id in range(args.max_steps): + _append_video_frames(video_frames, snapshot.values) + sample = sample_builder.build( + episode=episode, + tick_id=tick_id, + observations=observations, + runtime_description=runtime_description, + reward=reward_sum, + done=done, + success=success, + observation_values=snapshot.values, + ) + live.update_observation(sample) + command_values = live.next_action_values(timeout_s=float(args.live_chunk_timeout_s)) + if command_values is None: + stale_waits += 1 + consecutive_stale_waits += 1 + if consecutive_stale_waits >= int(args.live_max_stale_waits): + break + continue + consecutive_stale_waits = 0 + last_values = command_values + step_response = client.step( + StepRequest( + episode_id=episode.episode_id, + tick_id=tick_id, + action=RuntimeActionFrame( + frame_type="runtime_action", + space_id=_action_space_id(args), + values=list(command_values), + tick_id=tick_id, + ), + ) + ) + steps = tick_id + 1 + reward_sum += step_response.reward + done = step_response.done + success = step_response.success + snapshot = client.latest_observation_snapshot() + observations = snapshot.observations + observed_streams = snapshot.observed_streams + if done: + break + + _write_live_videos( + episode_dir, + episode.episode_id, + video_frames, + fps=int(args.control_step_hz), + ) + episode_success = bool(success) + failure_reason = None + if not episode_success: + if stale_waits >= int(args.live_max_stale_waits) and not last_values: + failure_reason = "live_action_timeout" + else: + failure_reason = "done_without_success" if done else "max_steps_without_success" + task_diagnostics = live.diagnostics() + inference_status_counts = task_diagnostics.get("inference_status_counts", {}) + if not isinstance(inference_status_counts, dict): + inference_status_counts = {} + diagnostics: dict[str, object] = { + "chunk_count": _int_diagnostic(task_diagnostics, "accepted_chunks", live.chunk_count), + "consumed_actions": _int_diagnostic( + task_diagnostics, "consumed_actions", live.consumed_actions + ), + "refill_triggers": _int_diagnostic(task_diagnostics, "refill_triggers", 0), + "inference_status_counts": { + str(key): int(value) + for key, value in inference_status_counts.items() + if isinstance(value, int) + }, + "stale_waits": stale_waits, + "live_max_stale_waits": int(args.live_max_stale_waits), + "stale_deactivations": _int_diagnostic( + task_diagnostics, "stale_deactivations", live.stale_deactivations + ), + "last_command_sequence": live.last_command_sequence, + } + return BenchmarkEpisodeResult( + episode_id=episode.episode_id, + task_id=episode.task_id, + task_index=episode.task_index, + init_state_index=episode.init_state_index, + success=episode_success, + steps=steps, + reward_sum=reward_sum, + done=done, + failure_reason=failure_reason, + action_shape=(len(last_values),), + action_min=min(last_values) if last_values else None, + action_max=max(last_values) if last_values else None, + observed_streams=observed_streams, + ), diagnostics + + +def _int_diagnostic(diagnostics: Mapping[str, object], key: str, default: int) -> int: + value = diagnostics.get(key) + return value if isinstance(value, int) else default + + +def _append_video_frames( + video_frames: dict[str, list[np.ndarray]], + stream_values: Mapping[str, object], +) -> None: + for stream, frames in video_frames.items(): + value = stream_values.get(stream) + if not isinstance(value, np.ndarray): + continue + if value.ndim != 3 or value.shape[2] != 3: + continue + frames.append(value.astype(np.uint8, copy=False)) + + +def _write_live_videos( + episode_dir: Path, + episode_id: str, + video_frames: dict[str, list[np.ndarray]], + *, + fps: int, +) -> None: + if fps <= 0: + raise ValueError("video fps must be positive") + for stream, frames in video_frames.items(): + if not frames: + continue + stream_name = stream.replace("/", "_") + video_path = episode_dir / "videos" / episode_id / f"{stream_name}.mp4" + video_path.parent.mkdir(parents=True, exist_ok=True) + imageio.mimsave(video_path, frames, fps=fps) # type: ignore[arg-type] + + +def _selected_episodes(limit: int | None) -> list[BenchmarkEpisodeSpec]: + episodes = libero_object_episode_matrix() + return episodes if limit is None else episodes[:limit] + + +def _policy_module(args: argparse.Namespace) -> RobotPolicyModule: + backend: PolicyBackend + if args.fake_backend: + backend = FixedActionBackend( + _fixed_action_values(args.fixed_action), + use_action_chunk=bool(args.live_policy_stream), + ) + return RobotPolicyModule( + backend=backend, + contract_type=_contract_type(args), + contract_params={}, + ) + else: + return RobotPolicyModule( + backend_type="lerobot", + backend_params={ + "policy_family": _policy_family(args), + "checkpoint_id": _checkpoint(args), + "device": args.device, + "use_action_chunk": bool(args.live_policy_stream), + }, + contract_type=_contract_type(args), + contract_params={}, + ) + + +def _start_runtime( + args: argparse.Namespace, episode: BenchmarkEpisodeSpec +) -> ModuleLiberoRuntimeSession: + blueprint = libero_pro_runtime_blueprint( + bddl_root=args.bddl_root, + init_states_root=args.init_states_root, + benchmark_name="libero_object", + robot_id="panda", + task_order_index=0, + task_index=episode.task_index, + init_state_index=episode.init_state_index, + action_mode=_runtime_action_mode(args), + camera_names=tuple(args.camera_names), + camera_height=args.camera_height, + camera_width=args.camera_width, + control_freq=args.control_step_hz, + horizon=args.max_steps, + seed=args.seed, + allow_asset_bootstrap=args.allow_asset_bootstrap, + visualize=args.visualize, + ).global_config(viewer="none", n_workers=1) + coordinator = ModuleCoordinator.build(blueprint) + runtime = coordinator.get_instance(LiberoProRuntimeModule) + return ModuleLiberoRuntimeSession( + runtime, coordinator, expected_image_streams=args.camera_names + ) + + +def _stop_runtime( + client: ModuleLiberoRuntimeSession, episode_dir: Path, cleanup_entry: dict[str, object] +) -> None: + client.close() + episode_dir.mkdir(parents=True, exist_ok=True) + cleanup_entry["runtime_stopped"] = True + + +def _summary( + results: Sequence[BenchmarkEpisodeResult], success_threshold: float +) -> BenchmarkEvaluationSummary: + successes = sum(1 for result in results if result.success) + success_rate = successes / len(results) if results else 0.0 + return BenchmarkEvaluationSummary( + episodes=len(results), + successes=successes, + success_rate=success_rate, + success_threshold=success_threshold, + passed=success_rate > success_threshold, + ) + + +def _write_aggregate_artifacts( + artifact_dir: Path, + results: Sequence[BenchmarkEpisodeResult], + summary: BenchmarkEvaluationSummary, + policy_module: DescribedPolicyModule, + args: argparse.Namespace, + cleanup: dict[str, object], +) -> None: + write_json(artifact_dir / "summary.json", _json_ready(summary)) + _write_jsonl(artifact_dir / "episodes.jsonl", results) + write_json( + artifact_dir / "checkpoint_metadata.json", _json_ready(policy_module.describe_backend()) + ) + if results: + _copy_if_present( + artifact_dir / "episodes" / results[0].episode_id / "runtime_description.json", + artifact_dir / "runtime_description.json", + ) + write_json( + artifact_dir / "run_config.json", + { + "policy_family": _policy_family(args), + "checkpoint": _checkpoint(args), + "contract_type": _contract_type(args), + "action_space_id": _action_space_id(args), + "runtime_action_mode": _runtime_action_mode(args), + "gate_stage": _gate_stage(args), + "documented_control_mode": _documented_control_mode(args), + "fake_backend": args.fake_backend, + "live_policy_stream": args.live_policy_stream, + "live_chunk_timeout_s": args.live_chunk_timeout_s, + "live_max_stale_waits": args.live_max_stale_waits, + "episodes": len(results), + "expected_gate_episodes": _expected_gate_episodes(args), + "success_threshold": args.success_threshold, + "enforce_gate": args.enforce_gate, + "save_videos": args.save_videos, + "video_streams": list(args.camera_names) if args.save_videos else [], + "camera_names": list(args.camera_names), + "camera_height": args.camera_height, + "camera_width": args.camera_width, + "max_steps": args.max_steps, + "bddl_root": str(args.bddl_root), + "init_states_root": str(args.init_states_root), + }, + ) + write_json(artifact_dir / "cleanup_status.json", cleanup) + + +def _write_setup_failure_artifacts( + artifact_dir: Path, + policy_module: DescribedPolicyModule | None, + args: argparse.Namespace, + cleanup: dict[str, object], + exc: Exception, +) -> None: + close_policy_module = False + if policy_module is None: + policy_module = _policy_module(args) + close_policy_module = True + write_json( + artifact_dir / "setup_error.json", + { + "error_type": type(exc).__name__, + "message": str(exc), + "traceback": traceback.format_exc(), + "gate_verified": False, + "policy_executed": False, + "policy_family": _policy_family(args), + "checkpoint": _checkpoint(args), + "contract_type": _contract_type(args), + "action_space_id": _action_space_id(args), + "runtime_action_mode": _runtime_action_mode(args), + "gate_stage": _gate_stage(args), + "expected_gate_episodes": _expected_gate_episodes(args), + "success_threshold": args.success_threshold, + "hint": "Check per-episode sidecar logs and prepare LIBERO assets before rerunning.", + }, + ) + write_json( + artifact_dir / "checkpoint_metadata.json", _json_ready(policy_module.describe_backend()) + ) + write_json( + artifact_dir / "run_config.json", + { + "policy_family": _policy_family(args), + "checkpoint": _checkpoint(args), + "contract_type": _contract_type(args), + "gate_stage": _gate_stage(args), + "documented_control_mode": _documented_control_mode(args), + "fake_backend": args.fake_backend, + "live_policy_stream": args.live_policy_stream, + "live_chunk_timeout_s": args.live_chunk_timeout_s, + "live_max_stale_waits": args.live_max_stale_waits, + "expected_gate_episodes": _expected_gate_episodes(args), + "success_threshold": args.success_threshold, + "enforce_gate": args.enforce_gate, + "save_videos": args.save_videos, + "video_streams": list(args.camera_names) if args.save_videos else [], + "camera_names": list(args.camera_names), + "camera_height": args.camera_height, + "camera_width": args.camera_width, + "max_steps": args.max_steps, + "bddl_root": str(args.bddl_root), + "init_states_root": str(args.init_states_root), + "allow_asset_bootstrap": args.allow_asset_bootstrap, + }, + ) + write_json(artifact_dir / "cleanup_status.json", cleanup) + if close_policy_module: + policy_module.close() + + +def _fixed_action_values(raw: str) -> tuple[float, ...]: + values = tuple(float(value.strip()) for value in raw.split(",") if value.strip()) + if len(values) != 7: + raise ValueError("--fixed-action must contain exactly 7 comma-separated values") + return values + + +def _policy_family(args: argparse.Namespace) -> str: + family = str(getattr(args, "policy_family", DEFAULT_POLICY_FAMILY)) + if family not in POLICY_FAMILY_DEFAULTS: + families = ", ".join(sorted(POLICY_FAMILY_DEFAULTS)) + raise ValueError(f"unsupported policy family {family!r}; expected one of {families}") + return family + + +def _policy_defaults(args: argparse.Namespace) -> Mapping[str, object]: + return POLICY_FAMILY_DEFAULTS[_policy_family(args)] + + +def _checkpoint(args: argparse.Namespace) -> str: + checkpoint = getattr(args, "checkpoint", None) + if checkpoint: + return str(checkpoint) + return str(_policy_defaults(args)["checkpoint"]) + + +def _contract_type(args: argparse.Namespace) -> str: + contract_type = getattr(args, "contract_type", None) + if contract_type: + return str(contract_type) + return str(_policy_defaults(args)["contract_type"]) + + +def _action_space_id(args: argparse.Namespace) -> str: + return str(_policy_defaults(args)["action_space_id"]) + + +def _runtime_action_mode(args: argparse.Namespace) -> str: + return str(_policy_defaults(args)["runtime_action_mode"]) + + +def _gate_stage(args: argparse.Namespace) -> str: + stage = getattr(args, "gate_stage", None) + if stage: + return str(stage) + return "live" if bool(getattr(args, "live_policy_stream", False)) else "benchmark" + + +def _expected_gate_episodes(args: argparse.Namespace) -> int: + key = ( + "expected_live_episodes" + if bool(getattr(args, "live_policy_stream", False)) + else "expected_benchmark_episodes" + ) + return int(str(_policy_defaults(args)[key])) + + +def _documented_control_mode(args: argparse.Namespace) -> str: + mode = _policy_defaults(args).get("documented_control_mode", "native") + return str(mode) + + +def _repo_path(path: Path) -> Path: + return path if path.is_absolute() else REPO_ROOT / path + + +def _resolve_asset_roots( + bddl_root: Path | None, init_states_root: Path | None +) -> tuple[Path, Path]: + explicit_bddl = _repo_path(bddl_root) if bddl_root is not None else None + explicit_init = _repo_path(init_states_root) if init_states_root is not None else None + if explicit_bddl is not None and explicit_init is not None: + return explicit_bddl, explicit_init + discovered = _runtime_venv_libero_asset_roots() + if discovered is None: + raise SystemExit( + "LIBERO assets were not provided and could not be found in the prepared " + "LIBERO runtime environment. Run `rtk uv sync` in " + "packages/dimos-libero-pro-sidecar or pass --bddl-root and --init-states-root." + ) + discovered_bddl, discovered_init = discovered + return explicit_bddl or discovered_bddl, explicit_init or discovered_init + + +def _runtime_venv_libero_asset_roots() -> tuple[Path, Path] | None: + runtime_project = LIBERO_PRO_SIDECAR_SRC.parent + site_packages_roots = sorted((runtime_project / ".venv" / "lib").glob("python*/site-packages")) + for site_packages in site_packages_roots: + package_root = site_packages / "libero" / "libero" + bddl_root = package_root / "bddl_files" + init_states_root = package_root / "init_files" + if bddl_root.exists() and init_states_root.exists(): + return bddl_root, init_states_root + return None + + +def _write_jsonl(path: Path, rows: Sequence[object]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("".join(json.dumps(_json_ready(row), sort_keys=True) + "\n" for row in rows)) + + +def _copy_if_present(source: Path, destination: Path) -> None: + if not source.exists(): + return + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(source.read_bytes()) + + +def _json_ready(value: object) -> object: + if is_dataclass(value) and not isinstance(value, type): + return _json_ready(asdict(value)) + model_dump = getattr(value, "model_dump", None) + if callable(model_dump): + return model_dump(mode="json") + if isinstance(value, dict): + return {str(key): _json_ready(item) for key, item in value.items()} + if isinstance(value, tuple): + return [_json_ready(item) for item in value] + if isinstance(value, list): + return [_json_ready(item) for item in value] + return value + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--artifact-dir", type=Path, default=DEFAULT_ARTIFACT_DIR) + parser.add_argument("--max-steps", type=int, default=1000) + parser.add_argument("--control-step-hz", type=int, default=20) + parser.add_argument("--success-threshold", type=float, default=0.50) + parser.add_argument( + "--policy-family", + choices=tuple(sorted(POLICY_FAMILY_DEFAULTS)), + default=DEFAULT_POLICY_FAMILY, + help="LeRobot policy family to load for real-policy gates.", + ) + parser.add_argument( + "--checkpoint", + default=None, + help="Policy checkpoint id/path. Defaults to the selected policy family checkpoint.", + ) + parser.add_argument( + "--contract-type", + default=None, + help="Robot policy contract type. Defaults to the selected policy family contract.", + ) + parser.add_argument( + "--gate-stage", + choices=("benchmark", "live"), + default=None, + help="Artifact stage label. Defaults to benchmark or live from --live-policy-stream.", + ) + parser.add_argument("--device", default=None) + parser.add_argument( + "--live-policy-stream", + action="store_true", + help="Run real policy chunks through RobotPolicyModule streams and ControlCoordinator.", + ) + parser.add_argument( + "--live-chunk-timeout-s", + type=float, + default=5.0, + help="Seconds to wait for each ControlCoordinator-emitted live policy action.", + ) + parser.add_argument( + "--live-max-stale-waits", + type=int, + default=3, + help="Abort an episode after this many consecutive live action timeouts.", + ) + parser.add_argument("--seed", type=int, default=7) + parser.add_argument("--episodes-limit", type=int, default=None) + parser.add_argument("--no-enforce-gate", dest="enforce_gate", action="store_false") + parser.set_defaults(enforce_gate=True) + parser.add_argument("--fake-backend", action="store_true") + parser.add_argument("--fixed-action", default="0,0,0,0,0,0,0") + parser.add_argument("--camera-name", action="append", dest="camera_names") + parser.add_argument("--camera-height", type=int, default=128) + parser.add_argument("--camera-width", type=int, default=128) + parser.add_argument("--bddl-root", type=Path, default=None) + parser.add_argument("--init-states-root", type=Path, default=None) + parser.add_argument("--allow-asset-bootstrap", action="store_true") + parser.add_argument("--visualize", action="store_true") + parser.add_argument( + "--save-videos", + action="store_true", + help="Reserve video artifacts for real runs; full videos/images are not saved by default.", + ) + return parser + + +def main() -> None: + parser = _parser() + args = parser.parse_args() + args.checkpoint = _checkpoint(args) + args.contract_type = _contract_type(args) + args.gate_stage = _gate_stage(args) + args.camera_names = tuple(args.camera_names or DEFAULT_CAMERAS) + args.bddl_root, args.init_states_root = _resolve_asset_roots( + args.bddl_root, args.init_states_root + ) + summary = run_policy_gate(args) + print( + f"episodes={summary.episodes} successes={summary.successes} " + f"success_rate={summary.success_rate:.3f} passed={summary.passed}" + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/benchmarks/test_demo_lerobot_libero_policy_rollout.py b/scripts/benchmarks/test_demo_lerobot_libero_policy_rollout.py new file mode 100644 index 0000000000..fd589a8d60 --- /dev/null +++ b/scripts/benchmarks/test_demo_lerobot_libero_policy_rollout.py @@ -0,0 +1,256 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from argparse import Namespace +import json +from pathlib import Path + +from dimos_runtime_protocol import ( + EpisodeResetRequest, + EpisodeResetResponse, + MotorStateFrame, + RuntimeActionFrame, + RuntimeDescription, + StepRequest, + StepResponse, +) +import numpy as np + +from dimos.control.coordinator import ControlCoordinator +from dimos.robot_learning.policy_rollout.evaluation import ( + BenchmarkEpisodeSpec, + PolicyEvalRuntimeSession, + RuntimeStreamSnapshot, +) +from dimos.robot_learning.policy_rollout.models import BackendBatch, RobotPolicyActionChunk +from dimos.robot_learning.policy_rollout.robot_policy_module import RobotPolicyModule +from dimos.robot_learning.policy_rollout.vla_jepa_libero_contract import ( + VLA_JEPA_LIBERO_ACTION_SPACE_ID, +) +from dimos.robot_learning.policy_rollout.xvla_libero_contract import ( + XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID, +) +from scripts.benchmarks.demo_lerobot_libero_policy_rollout import ( + FixedActionBackend, + _LivePolicyCoordinator, + _run_live_episode_loop, + run_policy_gate, +) + + +def _live_args() -> Namespace: + return Namespace( + fake_backend=True, + fixed_action="0.1,-0.2,0.3,-0.4,0.5,-0.6,0.7", + policy_family="vla_jepa", + checkpoint=None, + contract_type=None, + gate_stage=None, + device="cpu", + control_step_hz=100.0, + max_steps=1, + live_chunk_timeout_s=2.0, + live_max_stale_waits=3, + save_videos=False, + camera_names=(), + ) + + +class _FakeLiveRuntimeSession(PolicyEvalRuntimeSession): + def __init__(self) -> None: + self.last_action: RuntimeActionFrame | None = None + self._snapshot = RuntimeStreamSnapshot( + observations=(), + values={ + "agentview": np.zeros((128, 128, 3), dtype=np.uint8), + "robot0_eye_in_hand": np.zeros((128, 128, 3), dtype=np.uint8), + "robot_state": np.asarray( + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], dtype=np.float32 + ), + }, + ) + self.closed = False + + def reset(self, request: EpisodeResetRequest) -> EpisodeResetResponse: + return EpisodeResetResponse( + episode_id=request.episode_id, + runtime_description=RuntimeDescription( + runtime_id="fake-libero", + backend="fake", + robot_surfaces=[], + control_step_hz=20, + observation_streams=["agentview", "robot0_eye_in_hand", "robot_state"], + metadata={"language": "pick up the object"}, + ), + ) + + def step(self, request: StepRequest) -> StepResponse: + if not isinstance(request.action, RuntimeActionFrame): + raise TypeError(f"expected RuntimeActionFrame, got {type(request.action).__name__}") + self.last_action = request.action + return StepResponse( + episode_id=request.episode_id, + tick_id=request.tick_id, + motor_state=MotorStateFrame(robot_id="panda", names=[], q=[], dq=[], tau=[]), + reward=1.0, + done=True, + success=True, + ) + + def latest_observation_snapshot(self) -> RuntimeStreamSnapshot: + return self._snapshot + + def close(self) -> None: + self.closed = True + + +def test_fixed_action_backend_can_emit_single_action_chunk() -> None: + backend = FixedActionBackend((0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7), use_action_chunk=True) + backend.initialize() + + output = backend.infer_batch(BackendBatch(payload={}, metadata={})) + + assert output.output == ((0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7),) + assert output.metadata["use_action_chunk"] is True + + +def test_live_policy_coordinator_uses_module_stream_and_ref_wiring() -> None: + live = _LivePolicyCoordinator(args=_live_args()) + try: + live.start() + assert live._module_coordinator is not None + assert ( + "robot_policy_action_chunk", + RobotPolicyActionChunk, + ) in live._module_coordinator._transport_registry + assert ( + ControlCoordinator, + "_robot_policy", + ) in live._module_coordinator._resolved_module_refs + assert ( + live._module_coordinator._resolved_module_refs[(ControlCoordinator, "_robot_policy")] + is RobotPolicyModule + ) + finally: + live.close() + + +def test_xvla_configuration_can_run_synchronous_benchmark_without_control_coordinator( + tmp_path: Path, mocker +) -> None: + args = _xvla_args(tmp_path, live_policy_stream=False) + runtime = _FakeLiveRuntimeSession() + mocker.patch( + "scripts.benchmarks.demo_lerobot_libero_policy_rollout._start_runtime", + return_value=runtime, + ) + + summary = run_policy_gate(args) + + assert summary.passed is True + assert runtime.last_action is not None + assert runtime.last_action.space_id == XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID + assert runtime.closed is True + run_config = json.loads((tmp_path / "run_config.json").read_text()) + assert run_config["policy_family"] == "xvla" + assert run_config["checkpoint"] == "lerobot/xvla-libero" + assert run_config["contract_type"] == "xvla_libero" + assert run_config["action_space_id"] == XVLA_LIBERO_ABSOLUTE_ACTION_SPACE_ID + assert run_config["runtime_action_mode"] == "native_absolute" + assert run_config["gate_stage"] == "benchmark" + assert run_config["expected_gate_episodes"] == 10 + + +def test_live_episode_loop_steps_runtime_with_control_coordinator_action(tmp_path: Path) -> None: + runtime = _FakeLiveRuntimeSession() + live = _LivePolicyCoordinator(args=_live_args()) + try: + live.start() + result, diagnostics = _run_live_episode_loop( + _live_args(), + BenchmarkEpisodeSpec( + episode_id="episode-1", + task_id="libero_object", + task_index=0, + init_state_index=0, + ), + runtime, + live, + tmp_path, + ) + + assert result.success is True + assert result.action_shape == (7,) + assert runtime.last_action is not None + assert runtime.last_action.space_id == VLA_JEPA_LIBERO_ACTION_SPACE_ID + np.testing.assert_allclose( + runtime.last_action.values, + [0.1, -0.2, 0.3, -0.4, 0.5, -0.6, 0.7], + ) + assert diagnostics["consumed_actions"] == 1 + assert diagnostics["refill_triggers"] == 1 + inference_status_counts = diagnostics["inference_status_counts"] + assert isinstance(inference_status_counts, dict) + assert inference_status_counts["started"] == 1 + finally: + live.close() + + +def test_xvla_live_configuration_deploys_same_module_native_path() -> None: + live = _LivePolicyCoordinator(args=_xvla_args(Path("unused"), live_policy_stream=True)) + try: + live.start() + assert live._module_coordinator is not None + assert ( + "robot_policy_action_chunk", + RobotPolicyActionChunk, + ) in live._module_coordinator._transport_registry + assert ( + ControlCoordinator, + "_robot_policy", + ) in live._module_coordinator._resolved_module_refs + assert live.describe_backend().backend_type == "fixed_action" + finally: + live.close() + + +def _xvla_args(tmp_path: Path, *, live_policy_stream: bool) -> Namespace: + return Namespace( + fake_backend=True, + fixed_action="0.1,-0.2,0.3,-0.4,0.5,-0.6,0.7", + policy_family="xvla", + checkpoint=None, + contract_type=None, + gate_stage=None, + device="cpu", + control_step_hz=100.0, + max_steps=1, + live_policy_stream=live_policy_stream, + live_chunk_timeout_s=2.0, + live_max_stale_waits=3, + save_videos=False, + camera_names=(), + artifact_dir=tmp_path, + success_threshold=0.5, + enforce_gate=True, + episodes_limit=1, + bddl_root=tmp_path, + init_states_root=tmp_path, + allow_asset_bootstrap=False, + visualize=False, + camera_height=128, + camera_width=128, + ) diff --git a/uv.lock b/uv.lock index 7ab16893ea..9432934548 100644 --- a/uv.lock +++ b/uv.lock @@ -569,8 +569,8 @@ name = "can-motor-control" version = "0.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform != 'darwin'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_machine != 'aarch64' and sys_platform != 'darwin'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'win32'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/c1/d4a796df3a503c5b15d188fe65793631038db2f59f51c7863f8d776e7f3c/can_motor_control-0.0.2.tar.gz", hash = "sha256:7ca4942c680244ca8fe86f5dbb0cb870a4c36d786827ece1b7f2e56dfe186ee7", size = 114974, upload-time = "2026-06-07T01:33:14.484Z" } wheels = [ @@ -1369,7 +1369,7 @@ name = "cuda-bindings" version = "12.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "cuda-pathfinder", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/7a/d8/b546104b8da3f562c1ff8ab36d130c8fe1dd6a045ced80b4f6ad74f7d4e1/cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5", size = 12148218, upload-time = "2025-10-21T14:51:28.855Z" }, @@ -1554,6 +1554,8 @@ dependencies = [ { name = "cryptography" }, { name = "dimos-lcm" }, { name = "dimos-viewer" }, + { name = "imageio" }, + { name = "imageio-ffmpeg" }, { name = "llvmlite" }, { name = "lz4" }, { name = "numba" }, @@ -2045,6 +2047,8 @@ requires-dist = [ { name = "gtsam-extended", marker = "extra == 'mapping'", specifier = ">=4.3a1.post1" }, { name = "h5py", marker = "extra == 'learning'" }, { name = "hydra-core", marker = "extra == 'perception'", specifier = ">=1.3.0" }, + { name = "imageio", specifier = ">=2.37.0" }, + { name = "imageio-ffmpeg", specifier = ">=0.6.0" }, { name = "ipykernel", marker = "extra == 'misc'" }, { name = "jinja2", marker = "extra == 'web'", specifier = ">=3.1.6" }, { name = "langchain", marker = "extra == 'agents'", specifier = ">=1.2.3,<2" }, @@ -3337,6 +3341,20 @@ 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" }, ] +[[package]] +name = "imageio-ffmpeg" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/bd/c3343c721f2a1b0c9fc71c1aebf1966a3b7f08c2eea8ed5437a2865611d6/imageio_ffmpeg-0.6.0.tar.gz", hash = "sha256:e2556bed8e005564a9f925bb7afa4002d82770d6b08825078b7697ab88ba1755", size = 25210, upload-time = "2025-01-16T21:34:32.747Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/58/87ef68ac83f4c7690961bce288fd8e382bc5f1513860fc7f90a9c1c1c6bf/imageio_ffmpeg-0.6.0-py3-none-macosx_10_9_intel.macosx_10_9_x86_64.whl", hash = "sha256:9d2baaf867088508d4a3458e61eeb30e945c4ad8016025545f66c4b5aaef0a61", size = 24932969, upload-time = "2025-01-16T21:34:20.464Z" }, + { url = "https://files.pythonhosted.org/packages/40/5c/f3d8a657d362cc93b81aab8feda487317da5b5d31c0e1fdfd5e986e55d17/imageio_ffmpeg-0.6.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b1ae3173414b5fc5f538a726c4e48ea97edc0d2cdc11f103afee655c463fa742", size = 21113891, upload-time = "2025-01-16T21:34:00.277Z" }, + { url = "https://files.pythonhosted.org/packages/33/e7/1925bfbc563c39c1d2e82501d8372734a5c725e53ac3b31b4c2d081e895b/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1d47bebd83d2c5fc770720d211855f208af8a596c82d17730aa51e815cdee6dc", size = 25632706, upload-time = "2025-01-16T21:33:53.475Z" }, + { url = "https://files.pythonhosted.org/packages/a0/2d/43c8522a2038e9d0e7dbdf3a61195ecc31ca576fb1527a528c877e87d973/imageio_ffmpeg-0.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c7e46fcec401dd990405049d2e2f475e2b397779df2519b544b8aab515195282", size = 29498237, upload-time = "2025-01-16T21:34:13.726Z" }, + { url = "https://files.pythonhosted.org/packages/a0/13/59da54728351883c3c1d9fca1710ab8eee82c7beba585df8f25ca925f08f/imageio_ffmpeg-0.6.0-py3-none-win32.whl", hash = "sha256:196faa79366b4a82f95c0f4053191d2013f4714a715780f0ad2a68ff37483cc2", size = 19652251, upload-time = "2025-01-16T21:34:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c6/fa760e12a2483469e2bf5058c5faff664acf66cadb4df2ad6205b016a73d/imageio_ffmpeg-0.6.0-py3-none-win_amd64.whl", hash = "sha256:02fa47c83703c37df6bfe4896aab339013f62bf02c5ebf2dce6da56af04ffc0a", size = 31246824, upload-time = "2025-01-16T21:34:28.6Z" }, +] + [[package]] name = "importlib-metadata" version = "8.7.1" @@ -5322,7 +5340,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, @@ -5333,7 +5351,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, @@ -5360,9 +5378,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-cusparse-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cusparse-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, @@ -5373,7 +5391,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, @@ -5520,23 +5538,23 @@ name = "open3d" version = "0.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "addict" }, - { name = "configargparse" }, - { name = "dash" }, - { name = "flask" }, - { name = "matplotlib" }, - { name = "nbformat" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pillow" }, - { name = "pyquaternion" }, - { name = "pyyaml" }, - { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "tqdm" }, - { name = "werkzeug" }, + { name = "addict", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "configargparse", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "dash", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "flask", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "matplotlib", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "nbformat", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "pillow", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "pyquaternion", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "pyyaml", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "tqdm", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, + { name = "werkzeug", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/5c/4b/91e8a4100adf0ccd2f7ad21dd24c2e3d8f12925396528d0462cfb1735e5a/open3d-0.19.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:f7128ded206e07987cc29d0917195fb64033dea31e0d60dead3629b33d3c175f", size = 103086005, upload-time = "2025-01-08T07:25:56.755Z" }, @@ -5555,13 +5573,13 @@ name = "open3d-unofficial-arm" version = "0.19.0.post9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "configargparse" }, - { name = "dash" }, - { name = "flask" }, - { name = "nbformat" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "werkzeug" }, + { name = "configargparse", marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, + { name = "dash", marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, + { name = "flask", marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, + { name = "nbformat", marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'" }, + { name = "werkzeug", marker = "platform_machine == 'aarch64' and sys_platform == 'linux'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ec/f9/edcfaa213800ea278804402baa65693840bc7a323b3de8a31c54ce4e42c8/open3d_unofficial_arm-0.19.0.post9.tar.gz", hash = "sha256:ee300bd557f04750db6e47ccb6c6867c6dd6cfc04169dddeb92505da9ea739ef", size = 5327, upload-time = "2026-04-16T21:21:11.152Z" } wheels = [ @@ -7054,8 +7072,8 @@ name = "pyquaternion" version = "0.9.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/3d092aa20efaedacb89c3221a92c6491be5b28f618a2c36b52b53e7446c2/pyquaternion-0.9.9.tar.gz", hash = "sha256:b1f61af219cb2fe966b5fb79a192124f2e63a3f7a777ac3cadf2957b1a81bea8", size = 15530, upload-time = "2020-10-05T01:31:30.327Z" } wheels = [ @@ -7908,10 +7926,10 @@ resolution-markers = [ "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32'", ] dependencies = [ - { name = "joblib", marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, + { name = "joblib", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, + { name = "threadpoolctl", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64') or (python_full_version < '3.11' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ @@ -7955,10 +7973,10 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'", ] dependencies = [ - { name = "joblib", marker = "python_full_version >= '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, + { name = "joblib", marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, + { name = "threadpoolctl", marker = "(python_full_version >= '3.11' and platform_machine != 'aarch64') or (python_full_version >= '3.11' and sys_platform != 'linux')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } wheels = [ @@ -8859,7 +8877,7 @@ name = "triton" version = "3.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata", marker = "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'win32') or (python_full_version < '3.11' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" }, + { name = "importlib-metadata", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, @@ -9491,7 +9509,7 @@ name = "winrt-runtime" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "typing-extensions", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/16/dd/acdd527c1d890c8f852cc2af644aa6c160974e66631289420aa871b05e65/winrt_runtime-3.2.1.tar.gz", hash = "sha256:c8dca19e12b234ae6c3dadf1a4d0761b51e708457492c13beb666556958801ea", size = 21721, upload-time = "2025-06-06T14:40:27.593Z" } wheels = [ @@ -9511,7 +9529,7 @@ name = "winrt-windows-devices-bluetooth" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b2/a0/1c8a0c469abba7112265c6cb52f0090d08a67c103639aee71fc690e614b8/winrt_windows_devices_bluetooth-3.2.1.tar.gz", hash = "sha256:db496d2d92742006d5a052468fc355bf7bb49e795341d695c374746113d74505", size = 23732, upload-time = "2025-06-06T14:41:20.489Z" } wheels = [ @@ -9531,7 +9549,7 @@ name = "winrt-windows-devices-bluetooth-advertisement" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/fc/7ffe66ca4109b9e994b27c00f3d2d506e6e549e268791f755287ad9106d8/winrt_windows_devices_bluetooth_advertisement-3.2.1.tar.gz", hash = "sha256:0223852a7b7fa5c8dea3c6a93473bd783df4439b1ed938d9871f947933e574cc", size = 16906, upload-time = "2025-06-06T14:41:21.448Z" } wheels = [ @@ -9551,7 +9569,7 @@ name = "winrt-windows-devices-bluetooth-genericattributeprofile" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/21/aeeddc0eccdfbd25e543360b5cc093233e2eab3cdfb53ad3cabae1b5d04d/winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1.tar.gz", hash = "sha256:cdf6ddc375e9150d040aca67f5a17c41ceaf13a63f3668f96608bc1d045dde71", size = 38896, upload-time = "2025-06-06T14:41:22.687Z" } wheels = [ @@ -9571,7 +9589,7 @@ name = "winrt-windows-devices-enumeration" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/dd/75835bfbd063dffa152109727dedbd80f6e92ea284855f7855d48cdf31c9/winrt_windows_devices_enumeration-3.2.1.tar.gz", hash = "sha256:df316899e39bfc0ffc1f3cb0f5ee54d04e1d167fbbcc1484d2d5121449a935cf", size = 23538, upload-time = "2025-06-06T14:41:26.787Z" } wheels = [ @@ -9591,7 +9609,7 @@ name = "winrt-windows-devices-radios" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5e/02/9704ea359ad8b0d6faa1011f98fb477e8fb6eac5201f39d19e73c2407e7b/winrt_windows_devices_radios-3.2.1.tar.gz", hash = "sha256:4dc9b9d1501846049eb79428d64ec698d6476c27a357999b78a8331072e18a0b", size = 5908, upload-time = "2025-06-06T14:41:44.868Z" } wheels = [ @@ -9611,7 +9629,7 @@ name = "winrt-windows-foundation" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0c/55/098ce7ea0679efcc1298b269c48768f010b6c68f90c588f654ec874c8a74/winrt_windows_foundation-3.2.1.tar.gz", hash = "sha256:ad2f1fcaa6c34672df45527d7c533731fdf65b67c4638c2b4aca949f6eec0656", size = 30485, upload-time = "2025-06-06T14:41:53.344Z" } wheels = [ @@ -9631,7 +9649,7 @@ name = "winrt-windows-foundation-collections" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/62/d21e3f1eeb8d47077887bbf0c3882c49277a84d8f98f7c12bda64d498a07/winrt_windows_foundation_collections-3.2.1.tar.gz", hash = "sha256:0eff1ad0d8d763ad17e9e7bbd0c26a62b27215016393c05b09b046d6503ae6d5", size = 16043, upload-time = "2025-06-06T14:41:53.983Z" } wheels = [ @@ -9651,7 +9669,7 @@ name = "winrt-windows-storage-streams" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "winrt-runtime", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "winrt-runtime", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/00/50/f4488b07281566e3850fcae1021f0285c9653992f60a915e15567047db63/winrt_windows_storage_streams-3.2.1.tar.gz", hash = "sha256:476f522722751eb0b571bc7802d85a82a3cae8b1cce66061e6e758f525e7b80f", size = 34335, upload-time = "2025-06-06T14:43:23.905Z" } wheels = [