From 14b65db9525cb7f22d014166aa862d72c1d4cc7b Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 23 Jun 2025 16:05:41 +0200 Subject: [PATCH 01/40] Add robot descriptions --- examples/urdf/broken_fanuc.yml | 30 +++ examples/urdf/broken_poppy.yml | 30 +++ examples/urdf/franka.yml | 32 +++ examples/urdf/gen3.yml | 30 +++ examples/urdf/kuka.yml | 30 +++ examples/urdf/piper.yml | 32 +++ examples/urdf/so_arm101.yml | 32 +++ examples/urdf/ur5.yml | 30 +++ examples/urdf/z1.yml | 30 +++ .../dora_pytorch_kinematics/main.py | 168 ++++++------- node-hub/dora-rerun/Cargo.toml | 2 +- node-hub/dora-rerun/pyproject.toml | 5 +- node-hub/dora-rerun/src/urdf.rs | 38 ++- node-hub/gamepad/gamepad/main.py | 234 +++++++++--------- 14 files changed, 510 insertions(+), 213 deletions(-) create mode 100644 examples/urdf/broken_fanuc.yml create mode 100644 examples/urdf/broken_poppy.yml create mode 100644 examples/urdf/franka.yml create mode 100644 examples/urdf/gen3.yml create mode 100644 examples/urdf/kuka.yml create mode 100644 examples/urdf/piper.yml create mode 100644 examples/urdf/so_arm101.yml create mode 100644 examples/urdf/ur5.yml create mode 100644 examples/urdf/z1.yml diff --git a/examples/urdf/broken_fanuc.yml b/examples/urdf/broken_fanuc.yml new file mode 100644 index 00000000..469cd0d3 --- /dev/null +++ b/examples/urdf/broken_fanuc.yml @@ -0,0 +1,30 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_m710ic70: pytorch_kinematics/cmd_vel + env: + m710ic70_urdf: "fanuc_m710ic_description" + m710ic70_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "fanuc_m710ic_description" + END_EFFECTOR_LINK: "tool0" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." diff --git a/examples/urdf/broken_poppy.yml b/examples/urdf/broken_poppy.yml new file mode 100644 index 00000000..f8c39a81 --- /dev/null +++ b/examples/urdf/broken_poppy.yml @@ -0,0 +1,30 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_poppy_ergo_jr: pytorch_kinematics/cmd_vel + env: + poppy_ergo_jr_urdf: "poppy_ergo_jr_description" + poppy_ergo_jr_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "poppy_ergo_jr_description" + END_EFFECTOR_LINK: "section_5" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." diff --git a/examples/urdf/franka.yml b/examples/urdf/franka.yml new file mode 100644 index 00000000..4aef1345 --- /dev/null +++ b/examples/urdf/franka.yml @@ -0,0 +1,32 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_panda: pytorch_kinematics/cmd_vel + env: + panda_urdf: "panda_description" + panda_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "panda_description" + END_EFFECTOR_LINK: "panda_link8" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.01 + ROTATION_TOLERANCE: 0.01 diff --git a/examples/urdf/gen3.yml b/examples/urdf/gen3.yml new file mode 100644 index 00000000..71a8a1e4 --- /dev/null +++ b/examples/urdf/gen3.yml @@ -0,0 +1,30 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_jaco: pytorch_kinematics/cmd_vel + env: + jaco_urdf: "gen3_description" + jaco_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "gen3_description" + END_EFFECTOR_LINK: "j2n6s300_end_effector" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." diff --git a/examples/urdf/kuka.yml b/examples/urdf/kuka.yml new file mode 100644 index 00000000..a7668be7 --- /dev/null +++ b/examples/urdf/kuka.yml @@ -0,0 +1,30 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_iiwa14_primitive_collision: pytorch_kinematics/cmd_vel + env: + iiwa14_primitive_collision_urdf: "iiwa14_description" + iiwa14_primitive_collision_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "iiwa14_description" + END_EFFECTOR_LINK: "iiwa_link_7" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." diff --git a/examples/urdf/piper.yml b/examples/urdf/piper.yml new file mode 100644 index 00000000..e2ca4385 --- /dev/null +++ b/examples/urdf/piper.yml @@ -0,0 +1,32 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_piper_description: pytorch_kinematics/cmd_vel + env: + piper_description_urdf: "piper_description" + piper_description_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "piper_description" + END_EFFECTOR_LINK: "link6" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 diff --git a/examples/urdf/so_arm101.yml b/examples/urdf/so_arm101.yml new file mode 100644 index 00000000..d61e5ef4 --- /dev/null +++ b/examples/urdf/so_arm101.yml @@ -0,0 +1,32 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_so101_new_calib: pytorch_kinematics/cmd_vel + env: + so101_new_calib_urdf: "so_arm101_description" + so101_new_calib_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "so_arm101_description" + END_EFFECTOR_LINK: "gripper" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.01 + ROTATION_TOLERANCE: 0.01 diff --git a/examples/urdf/ur5.yml b/examples/urdf/ur5.yml new file mode 100644 index 00000000..cf00a4a4 --- /dev/null +++ b/examples/urdf/ur5.yml @@ -0,0 +1,30 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_ur5_robot: pytorch_kinematics/cmd_vel + env: + ur5_robot_urdf: "ur5_description" + ur5_robot_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "ur5_description" + END_EFFECTOR_LINK: "tool0" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." diff --git a/examples/urdf/z1.yml b/examples/urdf/z1.yml new file mode 100644 index 00000000..bdfd8a0d --- /dev/null +++ b/examples/urdf/z1.yml @@ -0,0 +1,30 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_z1: pytorch_kinematics/cmd_vel + env: + z1_urdf: "z1_description" + z1_transform: "0. 0. 0. 1. 0. 0. 0." + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "z1_description" + END_EFFECTOR_LINK: "link05" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." diff --git a/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py b/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py index bd5d5ec5..63ae2b7e 100644 --- a/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py +++ b/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py @@ -11,6 +11,8 @@ import torch from dora import Node from pytorch_kinematics.transforms.rotation_conversions import matrix_to_euler_angles +POSITION_TOLERANCE = float(os.getenv("POSITION_TOLERANCE", "0.005")) +ROTATION_TOLERANCE = float(os.getenv("ROTATION_TOLERANCE", "0.05")) # in radians TRANSFORM = np.array(os.getenv("TRANSFORM", "0. 0. 0. 1. 0. 0. 0.").split(" ")).astype( np.float32, ) # wxyz format @@ -229,18 +231,21 @@ class RobotKinematics: # Instantiate and run the IK solver (core pytorch_kinematics objects/methods) ik_solver = pk.PseudoInverseIK( self.chain, - max_iterations=1_000, + max_iterations=100_000, retry_configs=q_init, joint_limits=torch.tensor(self.chain.get_joint_limits()), early_stopping_any_converged=True, - early_stopping_no_improvement="all", + # early_stopping_no_improvement="all", debug=False, lr=0.05, - rot_tolerance=1e-4, - pos_tolerance=1e-3, + rot_tolerance=ROTATION_TOLERANCE, + pos_tolerance=POSITION_TOLERANCE, ) solution_angles = ik_solver.solve(target_pose) - if solution_angles.err_rot > 1e-3 or solution_angles.err_pos > 1e-2: + if ( + solution_angles.err_rot > ROTATION_TOLERANCE + or solution_angles.err_pos > POSITION_TOLERANCE + ): print( f"IK did not converge: pos_err={solution_angles.err_pos}, rot_err={solution_angles.err_rot} for target {target_pose}", ) @@ -357,99 +362,96 @@ def main(): robot = RobotKinematics( urdf_path=model, urdf="", end_effector_link=end_effector_link ) - last_known_state = None + last_known_state = robot._default_q for event in node: if event["type"] == "INPUT": metadata = event["metadata"] if event["id"] == "cmd_vel": - if last_known_state is not None: - target_vel = event["value"].to_numpy() # expect 100ms - # Apply Forward Kinematics - target = robot.compute_fk(last_known_state) - target = ( - np.array(get_xyz_rpy_array_from_transform3d(target)) - + target_vel - ) - target = pa.array(target.ravel(), type=pa.float32()) - target = pk.Transform3d( - pos=target[:3], - rot=pk.transforms.euler_angles_to_matrix( - torch.tensor(target[3:6]), - convention="XYZ", - ), + target_vel = event["value"].to_numpy() # expect 100ms + # Apply Forward Kinematics + target = robot.compute_fk(last_known_state) + target = ( + np.array(get_xyz_rpy_array_from_transform3d(target).detach()) + + target_vel / 10 + ) + target = pa.array(target.ravel(), type=pa.float32()) + target = pk.Transform3d( + pos=target[:3], + rot=pk.transforms.euler_angles_to_matrix( + torch.tensor(target[3:6]), + convention="XYZ", + ), + ) + rob_target = ROB_TF.inverse().compose(target) + solution = robot.compute_ik(rob_target, last_known_state) + if solution is None: + print( + "No IK Solution for :", + target, + "skipping this frame.", ) - rob_target = ROB_TF.inverse().compose(target) - solution = robot.compute_ik(rob_target, last_known_state) - if solution is None: - print( - "No IK Solution for :", - target, - "skipping this frame.", - ) - continue - solution = solution.numpy().ravel() - metadata["encoding"] = "jointstate" - last_known_state = solution - solution = pa.array(last_known_state) - node.send_output(event["id"], solution, metadata=metadata) + continue + solution = solution.numpy().ravel() + metadata["encoding"] = "jointstate" + last_known_state = solution + solution = pa.array(last_known_state) + node.send_output(event["id"], solution, metadata=metadata) else: match metadata["encoding"]: case "xyzquat": # Apply Inverse Kinematics - if last_known_state is not None: - target = event["value"].to_numpy() - target = target.astype(np.float32) - target = pk.Transform3d( - pos=target[:3], - rot=torch.tensor(target[3:7]), - ) - rob_target = ROB_TF.inverse().compose(target) - solution = robot.compute_ik(rob_target, last_known_state) - metadata["encoding"] = "jointstate" - last_known_state = solution.numpy().ravel() - solution = pa.array(last_known_state) - node.send_output(event["id"], solution, metadata=metadata) + target = event["value"].to_numpy() + target = target.astype(np.float32) + target = pk.Transform3d( + pos=target[:3], + rot=torch.tensor(target[3:7]), + ) + rob_target = ROB_TF.inverse().compose(target) + solution = robot.compute_ik(rob_target, last_known_state) + metadata["encoding"] = "jointstate" + last_known_state = solution.numpy().ravel() + solution = pa.array(last_known_state) + node.send_output(event["id"], solution, metadata=metadata) case "xyzrpy": # Apply Inverse Kinematics - if last_known_state is not None: - target = event["value"].to_numpy() - target = target.astype(np.float32) - target = pk.Transform3d( - pos=target[:3], - rot=pk.transforms.euler_angles_to_matrix( - torch.tensor(target[3:6]), - convention="XYZ", - ), + target = event["value"].to_numpy() + target = target.astype(np.float32) + target = pk.Transform3d( + pos=target[:3], + rot=pk.transforms.euler_angles_to_matrix( + torch.tensor(target[3:6]), + convention="XYZ", + ), + ) + rob_target = ROB_TF.inverse().compose(target) + solution = robot.compute_ik(rob_target, last_known_state) + if solution is None: + print( + "No IK Solution for :", + target, + "skipping this frame.", ) - rob_target = ROB_TF.inverse().compose(target) - solution = robot.compute_ik(rob_target, last_known_state) - if solution is None: - print( - "No IK Solution for :", - target, - "skipping this frame.", - ) - continue - - solution = solution.numpy().ravel() - delta_angles = ( - solution - last_known_state[: len(solution)] - ) # match with dof - - valid = np.all( - (delta_angles >= -np.pi) & (delta_angles <= np.pi), + continue + + solution = solution.numpy().ravel() + delta_angles = ( + solution - last_known_state[: len(solution)] + ) # match with dof + + valid = np.all( + (delta_angles >= -np.pi) & (delta_angles <= np.pi), + ) + if not valid: + print( + "IK solution is not valid, as the rotation are too wide. skipping.", ) - if not valid: - print( - "IK solution is not valid, as the rotation are too wide. skipping.", - ) - continue - metadata["encoding"] = "jointstate" - last_known_state = solution - solution = pa.array(last_known_state) - node.send_output(event["id"], solution, metadata=metadata) + continue + metadata["encoding"] = "jointstate" + last_known_state = solution + solution = pa.array(last_known_state) + node.send_output(event["id"], solution, metadata=metadata) case "jointstate": target = event["value"].to_numpy() last_known_state = target diff --git a/node-hub/dora-rerun/Cargo.toml b/node-hub/dora-rerun/Cargo.toml index 84c075e9..d777d312 100644 --- a/node-hub/dora-rerun/Cargo.toml +++ b/node-hub/dora-rerun/Cargo.toml @@ -17,7 +17,7 @@ python = ["pyo3"] dora-node-api = { workspace = true, features = ["tracing"] } eyre = "0.6.8" tokio = { version = "1.24.2", features = ["rt"] } -rerun = { version = "0.23.1", features = ["web_viewer", "image"] } +rerun = { version = "0.23.3", features = ["web_viewer", "image"] } ndarray = "0.15.6" k = "0.32" pyo3 = { workspace = true, features = [ diff --git a/node-hub/dora-rerun/pyproject.toml b/node-hub/dora-rerun/pyproject.toml index 42ec1643..778822da 100644 --- a/node-hub/dora-rerun/pyproject.toml +++ b/node-hub/dora-rerun/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] license = { text = "MIT" } requires-python = ">=3.10" -dependencies = ["rerun-loader-urdf", 'rerun_sdk==0.23.1'] +dependencies = ["rerun-loader-urdf", 'rerun_sdk>=0.23.1', "robot-descriptions"] scripts = { "dora-rerun" = "dora_rerun:py_main" } @@ -28,4 +28,5 @@ extend-select = [ ] [tool.uv.sources] -rerun-loader-urdf = { git = "https://github.com/haixuanTao/rerun-loader-python-example-urdf.git", branch = "patch-2" } +rerun-loader-urdf = { git = "https://github.com/dora-rs/rerun-loader-python-urdf.git" } +robot-descriptions = { git = "https://github.com/robot-descriptions/robot_descriptions.py.git" } diff --git a/node-hub/dora-rerun/src/urdf.rs b/node-hub/dora-rerun/src/urdf.rs index 0543b029..18d77313 100644 --- a/node-hub/dora-rerun/src/urdf.rs +++ b/node-hub/dora-rerun/src/urdf.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::{collections::HashMap, fmt::format, path::PathBuf}; use eyre::{Context, ContextCompat, Result}; use k::{nalgebra::Quaternion, Chain, Translation3, UnitQuaternion}; @@ -60,23 +60,39 @@ pub fn init_urdf(rec: &RecordingStream) -> Result>> { .collect::>(); let mut chains = HashMap::new(); for (key, urdf_path) in urdfs { - let path = key.replace("_urdf", ".urdf").replace("_URDF", ".urdf"); - let chain = k::Chain::::from_urdf_file(&urdf_path).context("Could not load URDF")?; + let urdf_path = if urdf_path.ends_with("_description") { + // Use robot description to get its path + let bash_cmd = format!("robot_descriptions pull {urdf_path}"); + let response = std::process::Command::new("bash") + .arg("-c") + .arg(bash_cmd) + .output() + .context("Failed to execute bash command. Are you sure `robot_descriptions` is installed?")? + .stdout; + let response_str = + String::from_utf8(response).context("Could not parse robot descriptions")?; + PathBuf::from(response_str.trim()) + } else { + // Use the path directly + PathBuf::from(urdf_path) + }; + let chain = k::Chain::::from_urdf_file(&urdf_path) + .context(format!("Could not load URDF {:#?}", urdf_path))?; + let path = key.replace("_urdf", ".urdf").replace("_URDF", ".urdf"); let transform = key.replace("_urdf", "_transform"); if PathBuf::from(&urdf_path).file_name() != PathBuf::from(&path).file_name() { return Err(eyre::eyre!( - "URDF path should be the same as the key without _urdf or _URDF. Got {} instead of {}", urdf_path, path + "URDF filename should be the same as the environment variable name and replacing the dot with a dash. Got {:#?} instead of {}", urdf_path, path )); } - if let Err(err) = rec.log_file_from_path(&urdf_path, None, false) { - println!("Could not log file: {}. Errored with {}", urdf_path, err); - println!("Make sure to install urdf loader with:"); - println!( - "pip install git+https://github.com/rerun-io/rerun-loader-python-example-urdf.git" - ) - }; + rec.log_file_from_path(&urdf_path, None, true) + .context(format!( + "Could not log URDF file {:#?} within rerun-urdf-loader", + urdf_path + ))?; + println!("Logging URDF file: {:#?}", urdf_path); // Get transform by replacing URDF_ with TRANSFORM_ if let Ok(transform) = std::env::var(transform) { diff --git a/node-hub/gamepad/gamepad/main.py b/node-hub/gamepad/gamepad/main.py index ad6385ea..e65f6d40 100644 --- a/node-hub/gamepad/gamepad/main.py +++ b/node-hub/gamepad/gamepad/main.py @@ -1,149 +1,151 @@ """Gamepad controller node for Dora.""" -from dora import Node -import pygame -import pyarrow as pa import json +import pyarrow as pa +import pygame +from dora import Node + + class Controller: """Controller mapping.""" def __init__(self): """Change this according to your controller mapping. Currently Logitech F710.""" self.axisNames = { - 'LEFT-X': 0, - 'LEFT-Y': 1, - 'RIGHT-X': 2, - 'RIGHT-Y': 3, + "LEFT-X": 0, + "LEFT-Y": 1, + "RIGHT-X": 2, + "RIGHT-Y": 3, } self.buttonNames = { - 'X': 0, - 'A': 1, - 'B': 2, - 'Y': 3, - 'LB': 4, - 'RB': 5, - 'LT': 6, - 'RT': 7, - 'BACK': 8, - 'START': 9, - 'LEFT-STICK': 10, - 'RIGHT-STICK': 11, + "X": 0, + "A": 1, + "B": 2, + "Y": 3, + "LB": 4, + "RB": 5, + "LT": 6, + "RT": 7, + "BACK": 8, + "START": 9, + "LEFT-STICK": 10, + "RIGHT-STICK": 11, } self.hatIndex = 0 # Index of the D-pad hat + def main(): node = Node("gamepad") - + pygame.init() pygame.joystick.init() - - if pygame.joystick.get_count() == 0: - print("No gamepad found! Please connect your controller.") - return - + + assert pygame.joystick.get_count(), ( + "No gamepad found! Please connect your controller." + ) + joystick = pygame.joystick.Joystick(0) joystick.init() controller = Controller() - + move_speed = 0.05 # Fixed increment for D-pad max_linear_z_speed = 0.1 # Maximum linear Z speed max_angular_speed = 0.8 # Maximum angular speed - + print(f"Detected controller: {joystick.get_name()}") print(f"Number of axes: {joystick.get_numaxes()}") print(f"Number of buttons: {joystick.get_numbuttons()}") print("Press Ctrl+C to exit") - try: - for event in node: - pygame.event.pump() - - # Get all controller states - axes = [joystick.get_axis(i) for i in range(joystick.get_numaxes())] - buttons = [joystick.get_button(i) for i in range(joystick.get_numbuttons())] - - # Get hat state (D-pad) - dpad_x, dpad_y = 0, 0 - if joystick.get_numhats() > 0: - dpad_x, dpad_y = joystick.get_hat(controller.hatIndex) - - # Create raw control state - raw_control = { - "axes": axes, - "buttons": buttons, - "hats": [[dpad_x, dpad_y]], - "mapping": { - "axes": controller.axisNames, - "buttons": controller.buttonNames - } - } - - # cmd_vel processing: - # 1. D-pad vertical for X axis - # 2. D-pad horizontal for Y axis - # 3. Right stick vertical for Z axis - # 4. Right stick horizontal for rotation around Z - # 5. Left stick vertical for rotation around X - # 6. Left stick horizontal for rotation around Y - - deadzone = 0.05 - - # Linear X velocity from D-pad vertical - linear_x = 0.0 - if dpad_y != 0: - linear_x = dpad_y * move_speed - - # Linear Y velocity from D-pad horizontal - linear_y = 0.0 - if dpad_x != 0: - linear_y = dpad_x * move_speed - - # Linear Z velocity from right stick vertical - right_y = -joystick.get_axis(controller.axisNames['RIGHT-Y']) - right_y = 0.0 if abs(right_y) < deadzone else right_y - linear_z = right_y * max_linear_z_speed - - # Angular Z velocity (rotation) from right stick horizontal - right_x = -joystick.get_axis(controller.axisNames['RIGHT-X']) - right_x = 0.0 if abs(right_x) < deadzone else right_x - angular_z = 0 * max_angular_speed # TODO: Make z non zero, but on my gamepad the value is never zero - - # Angular X velocity from left stick vertical - left_y = -joystick.get_axis(controller.axisNames['LEFT-Y']) - left_y = 0.0 if abs(left_y) < deadzone else left_y - angular_x = left_y * max_angular_speed - - # Angular Y velocity from left stick horizontal - left_x = -joystick.get_axis(controller.axisNames['LEFT-X']) - left_x = 0.0 if abs(left_x) < deadzone else left_x - angular_y = left_x * max_angular_speed - - cmd_vel = [linear_x, linear_y, linear_z, angular_x, angular_y, angular_z] - if any(cmd_vel): - node.send_output( - output_id="cmd_vel", - data=pa.array(cmd_vel, type=pa.float64()), - metadata={"type": "cmd_vel"} - ) - - node.send_output( - output_id="raw_control", - data=pa.array([json.dumps(raw_control)], type=pa.string()), - metadata={"type": "raw_control"} - ) - - except KeyboardInterrupt: - print("\nExiting...") - finally: - pygame.quit() - zero_cmd = [0.0] * 6 - node.send_output( - output_id="cmd_vel", - data=pa.array(zero_cmd, type=pa.float64()), - metadata={"type": "cmd_vel"} - ) + for event in node: + pygame.event.pump() + + # Get all controller states + axes = [joystick.get_axis(i) for i in range(joystick.get_numaxes())] + buttons = [joystick.get_button(i) for i in range(joystick.get_numbuttons())] + + # Get hat state (D-pad) + dpad_x, dpad_y = 0, 0 + if joystick.get_numhats() > 0: + dpad_x, dpad_y = joystick.get_hat(controller.hatIndex) + + # Create raw control state + raw_control = { + "axes": axes, + "buttons": buttons, + "hats": [[dpad_x, dpad_y]], + "mapping": { + "axes": controller.axisNames, + "buttons": controller.buttonNames, + }, + } + + # cmd_vel processing: + # 1. D-pad vertical for X axis + # 2. D-pad horizontal for Y axis + # 3. Right stick vertical for Z axis + # 4. Right stick horizontal for rotation around Z + # 5. Left stick vertical for rotation around X + # 6. Left stick horizontal for rotation around Y + + deadzone = 0.05 + + # Linear X velocity from D-pad vertical + linear_x = 0.0 + if dpad_y != 0: + linear_x = dpad_y * move_speed + + # Linear Y velocity from D-pad horizontal + linear_y = 0.0 + if dpad_x != 0: + linear_y = dpad_x * move_speed + + # Linear Z velocity from right stick vertical + right_y = -joystick.get_axis(controller.axisNames["RIGHT-Y"]) + right_y = 0.0 if abs(right_y) < deadzone else right_y + linear_z = right_y * max_linear_z_speed + + # Angular Z velocity (rotation) from right stick horizontal + right_x = -joystick.get_axis(controller.axisNames["RIGHT-X"]) + right_x = 0.0 if abs(right_x) < deadzone else right_x + angular_z = ( + right_x * max_angular_speed + ) # TODO: Make z non zero, but on my gamepad the value is never zero + + # Angular X velocity from left stick vertical + left_y = -joystick.get_axis(controller.axisNames["LEFT-Y"]) + left_y = 0.0 if abs(left_y) < deadzone else left_y + angular_x = left_y * max_angular_speed + + # Angular Y velocity from left stick horizontal + left_x = -joystick.get_axis(controller.axisNames["LEFT-X"]) + left_x = 0.0 if abs(left_x) < deadzone else left_x + angular_y = left_x * max_angular_speed + + cmd_vel = [linear_x, linear_y, linear_z, angular_x, angular_y, angular_z] + if any(cmd_vel): + node.send_output( + output_id="cmd_vel", + data=pa.array(cmd_vel, type=pa.float64()), + metadata={"type": "cmd_vel"}, + ) + + node.send_output( + output_id="raw_control", + data=pa.array([json.dumps(raw_control)], type=pa.string()), + metadata={"type": "raw_control"}, + ) + + pygame.quit() + zero_cmd = [0.0] * 6 + node.send_output( + output_id="cmd_vel", + data=pa.array(zero_cmd, type=pa.float64()), + metadata={"type": "cmd_vel"}, + ) + if __name__ == "__main__": - main() \ No newline at end of file + main() From c357415818dfa8a1b773ba0f19f49d206a664ab7 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Tue, 24 Jun 2025 15:52:40 +0200 Subject: [PATCH 02/40] Minor fix on robot descriptions version --- node-hub/dora-mujoco/pyproject.toml | 15 +++++++++------ node-hub/dora-rerun/src/urdf.rs | 5 +++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/node-hub/dora-mujoco/pyproject.toml b/node-hub/dora-mujoco/pyproject.toml index 055a73c3..c5de8d41 100644 --- a/node-hub/dora-mujoco/pyproject.toml +++ b/node-hub/dora-mujoco/pyproject.toml @@ -8,11 +8,11 @@ readme = "README.md" requires-python = ">=3.8" dependencies = [ - "dora-rs >= 0.3.9", - "mujoco >= 3.1.6", - "numpy >= 1.21.0", - "pyarrow >= 14.0.1", - "robot_descriptions >= 1.12.0", + "dora-rs >= 0.3.9", + "mujoco >= 3.1.6", + "numpy >= 1.21.0", + "pyarrow >= 14.0.1", + "robot_descriptions", ] [dependency-groups] @@ -23,9 +23,12 @@ dora-mujoco = "dora_mujoco.main:main" [tool.ruff.lint] extend-select = [ - "UP", # pyupgrade + "UP", # pyupgrade "PERF", # Ruff's PERF rule "RET", # Ruff's RET rule "RSE", # Ruff's RSE rule "N", # Ruff's N rule ] + +[tool.uv.sources] +robot-descriptions = { git = "https://github.com/robot-descriptions/robot_descriptions.py.git" } diff --git a/node-hub/dora-rerun/src/urdf.rs b/node-hub/dora-rerun/src/urdf.rs index 18d77313..4241df3d 100644 --- a/node-hub/dora-rerun/src/urdf.rs +++ b/node-hub/dora-rerun/src/urdf.rs @@ -71,6 +71,11 @@ pub fn init_urdf(rec: &RecordingStream) -> Result>> { .stdout; let response_str = String::from_utf8(response).context("Could not parse robot descriptions")?; + // Only keep last line of the response + let response_str = response_str + .lines() + .last() + .context("Could not find last line in robot descriptions response")?; PathBuf::from(response_str.trim()) } else { // Use the path directly From 7391291f689821e487e3e107eece421f55a61876 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Tue, 24 Jun 2025 18:18:25 +0200 Subject: [PATCH 03/40] Better configuration for each arm --- examples/keyboard/dataflow.yml | 14 +++++++++++++ examples/urdf/franka.yml | 7 +++++-- examples/urdf/kuka.yml | 3 +++ examples/urdf/piper.yml | 3 +++ examples/urdf/so_arm101.yml | 5 ++++- examples/urdf/ur5.yml | 5 +++++ examples/urdf/z1.yml | 7 ++++++- .../dora_pytorch_kinematics/main.py | 2 +- node-hub/gamepad/gamepad/main.py | 21 ++++++++++--------- 9 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 examples/keyboard/dataflow.yml diff --git a/examples/keyboard/dataflow.yml b/examples/keyboard/dataflow.yml new file mode 100644 index 00000000..34e303b3 --- /dev/null +++ b/examples/keyboard/dataflow.yml @@ -0,0 +1,14 @@ +nodes: + - id: keyboard + build: pip install -e ../../node-hub/dora-keyboard + path: dora-keyboard + outputs: + - char + env: + DISPLAY: $DISPLAY + + - id: rerun + path: dora-rerun + build: pip install -e ../../node-hub/dora-rerun + inputs: + text_input: keyboard/char diff --git a/examples/urdf/franka.yml b/examples/urdf/franka.yml index 4aef1345..59afa7b5 100644 --- a/examples/urdf/franka.yml +++ b/examples/urdf/franka.yml @@ -16,6 +16,9 @@ nodes: - raw_control inputs: tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 - id: pytorch_kinematics build: pip install -e ../../node-hub/dora-pytorch-kinematics @@ -28,5 +31,5 @@ nodes: MODEL_NAME: "panda_description" END_EFFECTOR_LINK: "panda_link8" TRANSFORM: "0. 0. 0. 1. 0. 0. 0." - POSITION_TOLERANCE: 0.01 - ROTATION_TOLERANCE: 0.01 + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 diff --git a/examples/urdf/kuka.yml b/examples/urdf/kuka.yml index a7668be7..0a46f861 100644 --- a/examples/urdf/kuka.yml +++ b/examples/urdf/kuka.yml @@ -16,6 +16,9 @@ nodes: - raw_control inputs: tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.02 + MAX_ANGULAR_SPEED: 0.10 - id: pytorch_kinematics build: pip install -e ../../node-hub/dora-pytorch-kinematics diff --git a/examples/urdf/piper.yml b/examples/urdf/piper.yml index e2ca4385..3055fb93 100644 --- a/examples/urdf/piper.yml +++ b/examples/urdf/piper.yml @@ -16,6 +16,9 @@ nodes: - raw_control inputs: tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 - id: pytorch_kinematics build: pip install -e ../../node-hub/dora-pytorch-kinematics diff --git a/examples/urdf/so_arm101.yml b/examples/urdf/so_arm101.yml index d61e5ef4..395c4591 100644 --- a/examples/urdf/so_arm101.yml +++ b/examples/urdf/so_arm101.yml @@ -16,6 +16,9 @@ nodes: - raw_control inputs: tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 - id: pytorch_kinematics build: pip install -e ../../node-hub/dora-pytorch-kinematics @@ -29,4 +32,4 @@ nodes: END_EFFECTOR_LINK: "gripper" TRANSFORM: "0. 0. 0. 1. 0. 0. 0." POSITION_TOLERANCE: 0.01 - ROTATION_TOLERANCE: 0.01 + ROTATION_TOLERANCE: 0.03 diff --git a/examples/urdf/ur5.yml b/examples/urdf/ur5.yml index cf00a4a4..0f19291a 100644 --- a/examples/urdf/ur5.yml +++ b/examples/urdf/ur5.yml @@ -16,6 +16,9 @@ nodes: - raw_control inputs: tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 - id: pytorch_kinematics build: pip install -e ../../node-hub/dora-pytorch-kinematics @@ -28,3 +31,5 @@ nodes: MODEL_NAME: "ur5_description" END_EFFECTOR_LINK: "tool0" TRANSFORM: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 diff --git a/examples/urdf/z1.yml b/examples/urdf/z1.yml index bdfd8a0d..bea243d2 100644 --- a/examples/urdf/z1.yml +++ b/examples/urdf/z1.yml @@ -16,6 +16,9 @@ nodes: - raw_control inputs: tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 - id: pytorch_kinematics build: pip install -e ../../node-hub/dora-pytorch-kinematics @@ -26,5 +29,7 @@ nodes: - cmd_vel env: MODEL_NAME: "z1_description" - END_EFFECTOR_LINK: "link05" + END_EFFECTOR_LINK: "link06" TRANSFORM: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 diff --git a/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py b/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py index 63ae2b7e..d1c795d4 100644 --- a/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py +++ b/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py @@ -374,7 +374,7 @@ def main(): target = robot.compute_fk(last_known_state) target = ( np.array(get_xyz_rpy_array_from_transform3d(target).detach()) - + target_vel / 10 + + target_vel ) target = pa.array(target.ravel(), type=pa.float32()) target = pk.Transform3d( diff --git a/node-hub/gamepad/gamepad/main.py b/node-hub/gamepad/gamepad/main.py index e65f6d40..6dd61280 100644 --- a/node-hub/gamepad/gamepad/main.py +++ b/node-hub/gamepad/gamepad/main.py @@ -5,7 +5,11 @@ import json import pyarrow as pa import pygame from dora import Node +import os +MAX_LINEAR_SPEED = float(os.getenv("MAX_LINEAR_SPEED", "0.05")) +MAX_ANGULAR_SPEED = float(os.getenv("MAX_ANGULAR_SPEED", "0.8")) +JOYSTICK_DEADZONE = float(os.getenv("JOYSTICK_DEADZONE", "0.2")) class Controller: """Controller mapping.""" @@ -50,9 +54,6 @@ def main(): controller = Controller() - move_speed = 0.05 # Fixed increment for D-pad - max_linear_z_speed = 0.1 # Maximum linear Z speed - max_angular_speed = 0.8 # Maximum angular speed print(f"Detected controller: {joystick.get_name()}") print(f"Number of axes: {joystick.get_numaxes()}") @@ -90,39 +91,39 @@ def main(): # 5. Left stick vertical for rotation around X # 6. Left stick horizontal for rotation around Y - deadzone = 0.05 + deadzone = JOYSTICK_DEADZONE # Linear X velocity from D-pad vertical linear_x = 0.0 if dpad_y != 0: - linear_x = dpad_y * move_speed + linear_x = dpad_y * MAX_LINEAR_SPEED # Linear Y velocity from D-pad horizontal linear_y = 0.0 if dpad_x != 0: - linear_y = dpad_x * move_speed + linear_y = dpad_x * MAX_LINEAR_SPEED # Linear Z velocity from right stick vertical right_y = -joystick.get_axis(controller.axisNames["RIGHT-Y"]) right_y = 0.0 if abs(right_y) < deadzone else right_y - linear_z = right_y * max_linear_z_speed + linear_z = right_y * MAX_LINEAR_SPEED # Angular Z velocity (rotation) from right stick horizontal right_x = -joystick.get_axis(controller.axisNames["RIGHT-X"]) right_x = 0.0 if abs(right_x) < deadzone else right_x angular_z = ( - right_x * max_angular_speed + right_x * MAX_ANGULAR_SPEED ) # TODO: Make z non zero, but on my gamepad the value is never zero # Angular X velocity from left stick vertical left_y = -joystick.get_axis(controller.axisNames["LEFT-Y"]) left_y = 0.0 if abs(left_y) < deadzone else left_y - angular_x = left_y * max_angular_speed + angular_x = left_y * MAX_ANGULAR_SPEED # Angular Y velocity from left stick horizontal left_x = -joystick.get_axis(controller.axisNames["LEFT-X"]) left_x = 0.0 if abs(left_x) < deadzone else left_x - angular_y = left_x * max_angular_speed + angular_y = left_x * MAX_ANGULAR_SPEED cmd_vel = [linear_x, linear_y, linear_z, angular_x, angular_y, angular_z] if any(cmd_vel): From f95bd230cca686b04875d693ae7a0bcb33420850 Mon Sep 17 00:00:00 2001 From: sjfhsjfh Date: Wed, 25 Jun 2025 17:15:41 +0800 Subject: [PATCH 04/40] refactor: cli commands --- binaries/cli/src/attach.rs | 2 +- binaries/cli/src/commands.rs | 102 +++ binaries/cli/src/{ => commands}/build.rs | 20 +- binaries/cli/src/{ => commands}/check.rs | 42 +- binaries/cli/src/commands/coordinator.rs | 66 ++ binaries/cli/src/commands/daemon.rs | 85 ++ binaries/cli/src/commands/destroy.rs | 28 + .../src/{graph/mod.rs => commands/graph.rs} | 32 +- binaries/cli/src/commands/graph/.gitignore | 1 + .../graph/mermaid-template.html | 0 binaries/cli/src/commands/list.rs | 59 ++ binaries/cli/src/commands/logs.rs | 90 ++ binaries/cli/src/commands/new.rs | 21 + binaries/cli/src/commands/run.rs | 42 + binaries/cli/src/commands/runtime.rs | 15 + binaries/cli/src/commands/self_.rs | 139 +++ binaries/cli/src/commands/start.rs | 127 +++ binaries/cli/src/commands/stop.rs | 111 +++ binaries/cli/src/{ => commands}/up.rs | 21 +- binaries/cli/src/common.rs | 69 ++ binaries/cli/src/lib.rs | 806 +----------------- binaries/cli/src/logs.rs | 45 - 22 files changed, 1069 insertions(+), 854 deletions(-) create mode 100644 binaries/cli/src/commands.rs rename binaries/cli/src/{ => commands}/build.rs (88%) rename binaries/cli/src/{ => commands}/check.rs (59%) create mode 100644 binaries/cli/src/commands/coordinator.rs create mode 100644 binaries/cli/src/commands/daemon.rs create mode 100644 binaries/cli/src/commands/destroy.rs rename binaries/cli/src/{graph/mod.rs => commands/graph.rs} (69%) create mode 100644 binaries/cli/src/commands/graph/.gitignore rename binaries/cli/src/{ => commands}/graph/mermaid-template.html (100%) create mode 100644 binaries/cli/src/commands/list.rs create mode 100644 binaries/cli/src/commands/logs.rs create mode 100644 binaries/cli/src/commands/new.rs create mode 100644 binaries/cli/src/commands/run.rs create mode 100644 binaries/cli/src/commands/runtime.rs create mode 100644 binaries/cli/src/commands/self_.rs create mode 100644 binaries/cli/src/commands/start.rs create mode 100644 binaries/cli/src/commands/stop.rs rename binaries/cli/src/{ => commands}/up.rs (88%) create mode 100644 binaries/cli/src/common.rs delete mode 100644 binaries/cli/src/logs.rs diff --git a/binaries/cli/src/attach.rs b/binaries/cli/src/attach.rs index 39c3a056..ee5e441f 100644 --- a/binaries/cli/src/attach.rs +++ b/binaries/cli/src/attach.rs @@ -15,7 +15,7 @@ use std::{path::PathBuf, sync::mpsc, time::Duration}; use tracing::{error, info}; use uuid::Uuid; -use crate::handle_dataflow_result; +use crate::common::handle_dataflow_result; pub fn attach_dataflow( dataflow: Descriptor, diff --git a/binaries/cli/src/commands.rs b/binaries/cli/src/commands.rs new file mode 100644 index 00000000..7ab3804a --- /dev/null +++ b/binaries/cli/src/commands.rs @@ -0,0 +1,102 @@ +mod build; +mod check; +mod coordinator; +mod daemon; +mod destroy; +mod graph; +mod list; +mod logs; +mod new; +mod run; +mod runtime; +mod self_; +mod start; +mod stop; +mod up; + +use build::Build; +use check::Check; +use coordinator::Coordinator; +use daemon::Daemon; +use destroy::Destroy; +use eyre::Context; +use graph::Graph; +use list::ListArgs; +use logs::LogsArgs; +use new::NewArgs; +use run::Run; +use runtime::Runtime; +use self_::SelfSubCommand; +use start::Start; +use stop::Stop; +use up::Up; + +/// dora-rs cli client +#[derive(Debug, clap::Subcommand)] +pub enum Command { + Check(Check), + Graph(Graph), + Build(Build), + New(NewArgs), + Run(Run), + Up(Up), + Destroy(Destroy), + Start(Start), + Stop(Stop), + List(ListArgs), + // Planned for future releases: + // Dashboard, + #[command(allow_missing_positional = true)] + Logs(LogsArgs), + // Metrics, + // Stats, + // Get, + // Upgrade, + Daemon(Daemon), + Runtime(Runtime), + Coordinator(Coordinator), + + Self_ { + #[clap(subcommand)] + command: SelfSubCommand, + }, +} + +fn default_tracing() -> eyre::Result<()> { + #[cfg(feature = "tracing")] + { + use dora_tracing::TracingBuilder; + + TracingBuilder::new("dora-cli") + .with_stdout("warn") + .build() + .wrap_err("failed to set up tracing subscriber")?; + } + Ok(()) +} + +pub trait Executable { + fn execute(self) -> eyre::Result<()>; +} + +impl Executable for Command { + fn execute(self) -> eyre::Result<()> { + match self { + Command::Check(args) => args.execute(), + Command::Coordinator(args) => args.execute(), + Command::Graph(args) => args.execute(), + Command::Build(args) => args.execute(), + Command::New(args) => args.execute(), + Command::Run(args) => args.execute(), + Command::Up(args) => args.execute(), + Command::Destroy(args) => args.execute(), + Command::Start(args) => args.execute(), + Command::Stop(args) => args.execute(), + Command::List(args) => args.execute(), + Command::Logs(args) => args.execute(), + Command::Daemon(args) => args.execute(), + Command::Self_ { command } => command.execute(), + Command::Runtime(args) => args.execute(), + } + } +} diff --git a/binaries/cli/src/build.rs b/binaries/cli/src/commands/build.rs similarity index 88% rename from binaries/cli/src/build.rs rename to binaries/cli/src/commands/build.rs index 3e8ffb6d..d781142e 100644 --- a/binaries/cli/src/build.rs +++ b/binaries/cli/src/commands/build.rs @@ -1,3 +1,5 @@ +use super::{default_tracing, Executable}; +use crate::common::resolve_dataflow; use dora_core::{ config::OperatorId, descriptor::{Descriptor, DescriptorExt, NodeExt, SINGLE_OPERATOR_DEFAULT_ID}, @@ -6,7 +8,23 @@ use dora_message::descriptor::EnvValue; use eyre::{eyre, Context}; use std::{collections::BTreeMap, path::Path, process::Command}; -use crate::resolve_dataflow; +#[derive(Debug, clap::Args)] +/// Run build commands provided in the given dataflow. +pub struct Build { + /// Path to the dataflow descriptor file + #[clap(value_name = "PATH")] + dataflow: String, + // Use UV to build nodes. + #[clap(long, action)] + uv: bool, +} + +impl Executable for Build { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + build(self.dataflow, self.uv) + } +} pub fn build(dataflow: String, uv: bool) -> eyre::Result<()> { let dataflow = resolve_dataflow(dataflow).context("could not resolve dataflow")?; diff --git a/binaries/cli/src/check.rs b/binaries/cli/src/commands/check.rs similarity index 59% rename from binaries/cli/src/check.rs rename to binaries/cli/src/commands/check.rs index 86cd6f7c..27b5fcbd 100644 --- a/binaries/cli/src/check.rs +++ b/binaries/cli/src/commands/check.rs @@ -1,11 +1,15 @@ -use crate::connect_to_coordinator; +use super::{default_tracing, Executable}; +use crate::{common::connect_to_coordinator, LOCALHOST}; use communication_layer_request_reply::TcpRequestReplyConnection; +use dora_core::descriptor::DescriptorExt; +use dora_core::{descriptor::Descriptor, topics::DORA_COORDINATOR_PORT_CONTROL_DEFAULT}; use dora_message::{cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply}; use eyre::{bail, Context}; use std::{ io::{IsTerminal, Write}, net::SocketAddr, }; +use std::{net::IpAddr, path::PathBuf}; use termcolor::{Color, ColorChoice, ColorSpec, WriteColor}; pub fn check_environment(coordinator_addr: SocketAddr) -> eyre::Result<()> { @@ -75,3 +79,39 @@ pub fn daemon_running(session: &mut TcpRequestReplyConnection) -> Result, + /// Address of the dora coordinator + #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] + coordinator_addr: IpAddr, + /// Port number of the coordinator control server + #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] + coordinator_port: u16, +} + +impl Executable for Check { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + + match self.dataflow { + Some(dataflow) => { + let working_dir = dataflow + .canonicalize() + .context("failed to canonicalize dataflow path")? + .parent() + .ok_or_else(|| eyre::eyre!("dataflow path has no parent dir"))? + .to_owned(); + Descriptor::blocking_read(&dataflow)?.check(&working_dir)?; + check_environment((self.coordinator_addr, self.coordinator_port).into())? + } + None => check_environment((self.coordinator_addr, self.coordinator_port).into())?, + } + + Ok(()) + } +} diff --git a/binaries/cli/src/commands/coordinator.rs b/binaries/cli/src/commands/coordinator.rs new file mode 100644 index 00000000..da48d1b3 --- /dev/null +++ b/binaries/cli/src/commands/coordinator.rs @@ -0,0 +1,66 @@ +use super::Executable; +use crate::LISTEN_WILDCARD; +use dora_coordinator::Event; +use dora_core::topics::{DORA_COORDINATOR_PORT_CONTROL_DEFAULT, DORA_COORDINATOR_PORT_DEFAULT}; + +#[cfg(feature = "tracing")] +use dora_tracing::TracingBuilder; + +use eyre::Context; +use std::net::{IpAddr, SocketAddr}; +use tokio::runtime::Builder; +use tracing::level_filters::LevelFilter; + +#[derive(Debug, clap::Args)] +/// Run coordinator +pub struct Coordinator { + /// Network interface to bind to for daemon communication + #[clap(long, default_value_t = LISTEN_WILDCARD)] + interface: IpAddr, + /// Port number to bind to for daemon communication + #[clap(long, default_value_t = DORA_COORDINATOR_PORT_DEFAULT)] + port: u16, + /// Network interface to bind to for control communication + #[clap(long, default_value_t = LISTEN_WILDCARD)] + control_interface: IpAddr, + /// Port number to bind to for control communication + #[clap(long, default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] + control_port: u16, + /// Suppresses all log output to stdout. + #[clap(long)] + quiet: bool, +} + +impl Executable for Coordinator { + fn execute(self) -> eyre::Result<()> { + #[cfg(feature = "tracing")] + { + let name = "dora-coordinator"; + let mut builder = TracingBuilder::new(name); + if !self.quiet { + builder = builder.with_stdout("info"); + } + builder = builder.with_file(name, LevelFilter::INFO)?; + builder + .build() + .wrap_err("failed to set up tracing subscriber")?; + } + + let rt = Builder::new_multi_thread() + .enable_all() + .build() + .context("tokio runtime failed")?; + rt.block_on(async { + let bind = SocketAddr::new(self.interface, self.port); + let bind_control = SocketAddr::new(self.control_interface, self.control_port); + let (port, task) = + dora_coordinator::start(bind, bind_control, futures::stream::empty::()) + .await?; + if !self.quiet { + println!("Listening for incoming daemon connection on {port}"); + } + task.await + }) + .context("failed to run dora-coordinator") + } +} diff --git a/binaries/cli/src/commands/daemon.rs b/binaries/cli/src/commands/daemon.rs new file mode 100644 index 00000000..a6350ea3 --- /dev/null +++ b/binaries/cli/src/commands/daemon.rs @@ -0,0 +1,85 @@ +use super::Executable; +use crate::common::handle_dataflow_result; +use dora_core::topics::{ + DORA_COORDINATOR_PORT_DEFAULT, DORA_DAEMON_LOCAL_LISTEN_PORT_DEFAULT, LOCALHOST, +}; + +#[cfg(feature = "tracing")] +use dora_tracing::TracingBuilder; + +use eyre::Context; +use std::{ + net::{IpAddr, SocketAddr}, + path::PathBuf, +}; +use tokio::runtime::Builder; +use tracing::level_filters::LevelFilter; + +#[derive(Debug, clap::Args)] +/// Run daemon +pub struct Daemon { + /// Unique identifier for the machine (required for distributed dataflows) + #[clap(long)] + machine_id: Option, + /// Local listen port for event such as dynamic node. + #[clap(long, default_value_t = DORA_DAEMON_LOCAL_LISTEN_PORT_DEFAULT)] + local_listen_port: u16, + /// Address and port number of the dora coordinator + #[clap(long, short, default_value_t = LOCALHOST)] + coordinator_addr: IpAddr, + /// Port number of the coordinator control server + #[clap(long, default_value_t = DORA_COORDINATOR_PORT_DEFAULT)] + coordinator_port: u16, + #[clap(long, hide = true)] + run_dataflow: Option, + /// Suppresses all log output to stdout. + #[clap(long)] + quiet: bool, +} + +impl Executable for Daemon { + fn execute(self) -> eyre::Result<()> { + #[cfg(feature = "tracing")] + { + let name = "dora-daemon"; + let filename = self + .machine_id + .as_ref() + .map(|id| format!("{name}-{id}")) + .unwrap_or(name.to_string()); + let mut builder = TracingBuilder::new(name); + if !self.quiet { + builder = builder.with_stdout("info,zenoh=warn"); + } + builder = builder.with_file(filename, LevelFilter::INFO)?; + builder + .build() + .wrap_err("failed to set up tracing subscriber")?; + } + + let rt = Builder::new_multi_thread() + .enable_all() + .build() + .context("tokio runtime failed")?; + rt.block_on(async { + match self.run_dataflow { + Some(dataflow_path) => { + tracing::info!("Starting dataflow `{}`", dataflow_path.display()); + if self.coordinator_addr != LOCALHOST { + tracing::info!( + "Not using coordinator addr {} as `run_dataflow` is for local dataflow only. Please use the `start` command for remote coordinator", + self.coordinator_addr + ); + } + + let result = dora_daemon::Daemon::run_dataflow(&dataflow_path, false).await?; + handle_dataflow_result(result, None) + } + None => { + dora_daemon::Daemon::run(SocketAddr::new(self.coordinator_addr, self.coordinator_port), self.machine_id, self.local_listen_port).await + } + } + }) + .context("failed to run dora-daemon") + } +} diff --git a/binaries/cli/src/commands/destroy.rs b/binaries/cli/src/commands/destroy.rs new file mode 100644 index 00000000..1a10222f --- /dev/null +++ b/binaries/cli/src/commands/destroy.rs @@ -0,0 +1,28 @@ +use super::{default_tracing, up, Executable}; +use dora_core::topics::{DORA_COORDINATOR_PORT_CONTROL_DEFAULT, LOCALHOST}; +use std::net::IpAddr; +use std::path::PathBuf; + +#[derive(Debug, clap::Args)] +/// Destroy running coordinator and daemon. If some dataflows are still running, they will be stopped first. +pub struct Destroy { + /// Use a custom configuration + #[clap(long, hide = true)] + config: Option, + /// Address of the dora coordinator + #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] + coordinator_addr: IpAddr, + /// Port number of the coordinator control server + #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] + coordinator_port: u16, +} + +impl Executable for Destroy { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + up::destroy( + self.config.as_deref(), + (self.coordinator_addr, self.coordinator_port).into(), + ) + } +} diff --git a/binaries/cli/src/graph/mod.rs b/binaries/cli/src/commands/graph.rs similarity index 69% rename from binaries/cli/src/graph/mod.rs rename to binaries/cli/src/commands/graph.rs index 0dbd55b6..33ae6510 100644 --- a/binaries/cli/src/graph/mod.rs +++ b/binaries/cli/src/commands/graph.rs @@ -1,11 +1,35 @@ -use std::{fs::File, io::Write, path::Path}; - +use super::Executable; use dora_core::descriptor::{Descriptor, DescriptorExt}; use eyre::Context; +use std::{ + fs::File, + io::Write, + path::{Path, PathBuf}, +}; + +const MERMAID_TEMPLATE: &str = include_str!("graph/mermaid-template.html"); + +#[derive(Debug, clap::Args)] +/// Generate a visualization of the given graph using mermaid.js. Use --open to open browser. +pub struct Graph { + /// Path to the dataflow descriptor file + #[clap(value_name = "PATH", value_hint = clap::ValueHint::FilePath)] + dataflow: PathBuf, + /// Visualize the dataflow as a Mermaid diagram (instead of HTML) + #[clap(long, action)] + mermaid: bool, + /// Open the HTML visualization in the browser + #[clap(long, action)] + open: bool, +} -const MERMAID_TEMPLATE: &str = include_str!("mermaid-template.html"); +impl Executable for Graph { + fn execute(self) -> eyre::Result<()> { + create(self.dataflow, self.mermaid, self.open) + } +} -pub(crate) fn create(dataflow: std::path::PathBuf, mermaid: bool, open: bool) -> eyre::Result<()> { +fn create(dataflow: std::path::PathBuf, mermaid: bool, open: bool) -> eyre::Result<()> { if mermaid { let visualized = visualize_as_mermaid(&dataflow)?; println!("{visualized}"); diff --git a/binaries/cli/src/commands/graph/.gitignore b/binaries/cli/src/commands/graph/.gitignore new file mode 100644 index 00000000..4be5659b --- /dev/null +++ b/binaries/cli/src/commands/graph/.gitignore @@ -0,0 +1 @@ +!*template.html \ No newline at end of file diff --git a/binaries/cli/src/graph/mermaid-template.html b/binaries/cli/src/commands/graph/mermaid-template.html similarity index 100% rename from binaries/cli/src/graph/mermaid-template.html rename to binaries/cli/src/commands/graph/mermaid-template.html diff --git a/binaries/cli/src/commands/list.rs b/binaries/cli/src/commands/list.rs new file mode 100644 index 00000000..fce836e4 --- /dev/null +++ b/binaries/cli/src/commands/list.rs @@ -0,0 +1,59 @@ +use std::io::Write; + +use super::{default_tracing, Executable}; +use crate::{ + common::{connect_to_coordinator, query_running_dataflows}, + LOCALHOST, +}; +use clap::Args; +use communication_layer_request_reply::TcpRequestReplyConnection; +use dora_core::topics::DORA_COORDINATOR_PORT_CONTROL_DEFAULT; +use dora_message::coordinator_to_cli::DataflowStatus; +use eyre::eyre; +use tabwriter::TabWriter; + +#[derive(Debug, Args)] +/// List running dataflows. +pub struct ListArgs { + /// Address of the dora coordinator + #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] + pub coordinator_addr: std::net::IpAddr, + /// Port number of the coordinator control server + #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] + pub coordinator_port: u16, +} + +impl Executable for ListArgs { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + + let mut session = + connect_to_coordinator((self.coordinator_addr, self.coordinator_port).into()) + .map_err(|_| eyre!("Failed to connect to coordinator"))?; + + list(&mut *session) + } +} + +fn list(session: &mut TcpRequestReplyConnection) -> Result<(), eyre::ErrReport> { + let list = query_running_dataflows(session)?; + + let mut tw = TabWriter::new(vec![]); + tw.write_all(b"UUID\tName\tStatus\n")?; + for entry in list.0 { + let uuid = entry.id.uuid; + let name = entry.id.name.unwrap_or_default(); + let status = match entry.status { + DataflowStatus::Running => "Running", + DataflowStatus::Finished => "Succeeded", + DataflowStatus::Failed => "Failed", + }; + tw.write_all(format!("{uuid}\t{name}\t{status}\n").as_bytes())?; + } + tw.flush()?; + let formatted = String::from_utf8(tw.into_inner()?)?; + + println!("{formatted}"); + + Ok(()) +} diff --git a/binaries/cli/src/commands/logs.rs b/binaries/cli/src/commands/logs.rs new file mode 100644 index 00000000..06496ad6 --- /dev/null +++ b/binaries/cli/src/commands/logs.rs @@ -0,0 +1,90 @@ +use super::{default_tracing, Executable}; +use crate::common::{connect_to_coordinator, query_running_dataflows}; +use bat::{Input, PrettyPrinter}; +use clap::Args; +use communication_layer_request_reply::TcpRequestReplyConnection; +use dora_core::topics::{DORA_COORDINATOR_PORT_CONTROL_DEFAULT, LOCALHOST}; +use dora_message::{cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply}; +use eyre::{bail, Context, Result}; +use uuid::Uuid; + +#[derive(Debug, Args)] +/// Show logs of a given dataflow and node. +pub struct LogsArgs { + /// Identifier of the dataflow + #[clap(value_name = "UUID_OR_NAME")] + pub dataflow: Option, + /// Show logs for the given node + #[clap(value_name = "NAME")] + pub node: String, + /// Address of the dora coordinator + #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] + pub coordinator_addr: std::net::IpAddr, + /// Port number of the coordinator control server + #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] + pub coordinator_port: u16, +} + +impl Executable for LogsArgs { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + + let mut session = + connect_to_coordinator((self.coordinator_addr, self.coordinator_port).into()) + .wrap_err("failed to connect to dora coordinator")?; + let list = + query_running_dataflows(&mut *session).wrap_err("failed to query running dataflows")?; + if let Some(dataflow) = self.dataflow { + let uuid = Uuid::parse_str(&dataflow).ok(); + let name = if uuid.is_some() { None } else { Some(dataflow) }; + logs(&mut *session, uuid, name, self.node) + } else { + let active = list.get_active(); + let uuid = match &active[..] { + [] => bail!("No dataflows are running"), + [uuid] => uuid.clone(), + _ => inquire::Select::new("Choose dataflow to show logs:", active).prompt()?, + }; + logs(&mut *session, Some(uuid.uuid), None, self.node) + } + } +} + +pub fn logs( + session: &mut TcpRequestReplyConnection, + uuid: Option, + name: Option, + node: String, +) -> Result<()> { + let logs = { + let reply_raw = session + .request( + &serde_json::to_vec(&ControlRequest::Logs { + uuid, + name, + node: node.clone(), + }) + .wrap_err("")?, + ) + .wrap_err("failed to send Logs request message")?; + + let reply = serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + match reply { + ControlRequestReply::Logs(logs) => logs, + other => bail!("unexpected reply to daemon logs: {other:?}"), + } + }; + + PrettyPrinter::new() + .header(false) + .grid(false) + .line_numbers(false) + .paging_mode(bat::PagingMode::QuitIfOneScreen) + .inputs(vec![Input::from_bytes(&logs) + .name("Logs") + .title(format!("Logs from {node}.").as_str())]) + .print() + .wrap_err("Something went wrong with viewing log file")?; + + Ok(()) +} diff --git a/binaries/cli/src/commands/new.rs b/binaries/cli/src/commands/new.rs new file mode 100644 index 00000000..0500ffb5 --- /dev/null +++ b/binaries/cli/src/commands/new.rs @@ -0,0 +1,21 @@ +use clap::Args; + +use super::{default_tracing, Executable}; + +#[derive(Debug, Args)] +/// Generate a new project or node. Choose the language between Rust, Python, C or C++. +pub struct NewArgs { + #[clap(flatten)] + // TODO!: better impl + args: crate::CommandNew, + /// Internal flag for creating with path dependencies + #[clap(hide = true, long)] + pub internal_create_with_path_dependencies: bool, +} + +impl Executable for NewArgs { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + crate::template::create(self.args, self.internal_create_with_path_dependencies) + } +} diff --git a/binaries/cli/src/commands/run.rs b/binaries/cli/src/commands/run.rs new file mode 100644 index 00000000..e35baeaa --- /dev/null +++ b/binaries/cli/src/commands/run.rs @@ -0,0 +1,42 @@ +use super::Executable; +use crate::common::{handle_dataflow_result, resolve_dataflow}; +use dora_daemon::Daemon; +use dora_tracing::TracingBuilder; +use eyre::Context; +use tokio::runtime::Builder; + +#[derive(Debug, clap::Args)] +/// Run a dataflow locally. +/// +/// Directly runs the given dataflow without connecting to a dora +/// coordinator or daemon. The dataflow is executed on the local machine. +pub struct Run { + /// Path to the dataflow descriptor file + #[clap(value_name = "PATH")] + dataflow: String, + // Use UV to run nodes. + #[clap(long, action)] + uv: bool, +} + +impl Executable for Run { + fn execute(self) -> eyre::Result<()> { + #[cfg(feature = "tracing")] + { + let log_level = std::env::var("RUST_LOG").ok().unwrap_or("info".to_string()); + TracingBuilder::new("run") + .with_stdout(log_level) + .build() + .wrap_err("failed to set up tracing subscriber")?; + } + + let dataflow_path = + resolve_dataflow(self.dataflow).context("could not resolve dataflow")?; + let rt = Builder::new_multi_thread() + .enable_all() + .build() + .context("tokio runtime failed")?; + let result = rt.block_on(Daemon::run_dataflow(&dataflow_path, self.uv))?; + handle_dataflow_result(result, None) + } +} diff --git a/binaries/cli/src/commands/runtime.rs b/binaries/cli/src/commands/runtime.rs new file mode 100644 index 00000000..430e3891 --- /dev/null +++ b/binaries/cli/src/commands/runtime.rs @@ -0,0 +1,15 @@ +use eyre::Context; + +use super::Executable; + +#[derive(Debug, clap::Args)] +/// Run runtime +pub struct Runtime; + +impl Executable for Runtime { + fn execute(self) -> eyre::Result<()> { + // No tracing: Do not set the runtime in the cli. + // ref: 72b4be808122574fcfda69650954318e0355cc7b cli::run + dora_runtime::main().context("Failed to run dora-runtime") + } +} diff --git a/binaries/cli/src/commands/self_.rs b/binaries/cli/src/commands/self_.rs new file mode 100644 index 00000000..097a7f4b --- /dev/null +++ b/binaries/cli/src/commands/self_.rs @@ -0,0 +1,139 @@ +use super::{default_tracing, Executable}; +use clap::Subcommand; +use eyre::{bail, Context}; + +#[derive(Debug, Subcommand)] +/// Dora CLI self-management commands +pub enum SelfSubCommand { + /// Check for updates or update the CLI + Update { + /// Only check for updates without installing + #[clap(long)] + check_only: bool, + }, + /// Remove The Dora CLI from the system + Uninstall { + /// Force uninstallation without confirmation + #[clap(long)] + force: bool, + }, +} + +impl Executable for SelfSubCommand { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + + match self { + SelfSubCommand::Update { check_only } => { + println!("Checking for updates..."); + + #[cfg(target_os = "linux")] + let bin_path_in_archive = format!("dora-cli-{}/dora", env!("TARGET")); + #[cfg(target_os = "macos")] + let bin_path_in_archive = format!("dora-cli-{}/dora", env!("TARGET")); + #[cfg(target_os = "windows")] + let bin_path_in_archive = String::from("dora.exe"); + + let status = self_update::backends::github::Update::configure() + .repo_owner("dora-rs") + .repo_name("dora") + .bin_path_in_archive(&bin_path_in_archive) + .bin_name("dora") + .show_download_progress(true) + .current_version(env!("CARGO_PKG_VERSION")) + .build()?; + + if check_only { + // Only check if an update is available + match status.get_latest_release() { + Ok(release) => { + let current_version = self_update::cargo_crate_version!(); + if current_version != release.version { + println!( + "An update is available: {}. Run 'dora self update' to update", + release.version + ); + } else { + println!( + "Dora CLI is already at the latest version: {}", + current_version + ); + } + } + Err(e) => println!("Failed to check for updates: {}", e), + } + } else { + // Perform the actual update + match status.update() { + Ok(update_status) => match update_status { + self_update::Status::UpToDate(version) => { + println!("Dora CLI is already at the latest version: {}", version); + } + self_update::Status::Updated(version) => { + println!("Successfully updated Dora CLI to version: {}", version); + } + }, + Err(e) => println!("Failed to update: {}", e), + } + } + } + SelfSubCommand::Uninstall { force } => { + if !force { + let confirmed = + inquire::Confirm::new("Are you sure you want to uninstall Dora CLI?") + .with_default(false) + .prompt() + .wrap_err("Uninstallation cancelled")?; + + if !confirmed { + println!("Uninstallation cancelled"); + return Ok(()); + } + } + + println!("Uninstalling Dora CLI..."); + #[cfg(feature = "python")] + { + println!("Detected Python installation..."); + + // Try uv pip uninstall first + let uv_status = std::process::Command::new("uv") + .args(["pip", "uninstall", "dora-rs-cli"]) + .status(); + + if let Ok(status) = uv_status { + if status.success() { + println!("Dora CLI has been successfully uninstalled via uv pip."); + return Ok(()); + } + } + + // Fall back to regular pip uninstall + println!("Trying with pip..."); + let status = std::process::Command::new("pip") + .args(["uninstall", "-y", "dora-rs-cli"]) + .status() + .wrap_err("Failed to run pip uninstall")?; + + if status.success() { + println!("Dora CLI has been successfully uninstalled via pip."); + } else { + bail!("Failed to uninstall Dora CLI via pip."); + } + } + #[cfg(not(feature = "python"))] + { + match self_replace::self_delete() { + Ok(_) => { + println!("Dora CLI has been successfully uninstalled."); + } + Err(e) => { + bail!("Failed to uninstall Dora CLI: {}", e); + } + } + } + } + } + Ok(()) + } +} diff --git a/binaries/cli/src/commands/start.rs b/binaries/cli/src/commands/start.rs new file mode 100644 index 00000000..fdee8709 --- /dev/null +++ b/binaries/cli/src/commands/start.rs @@ -0,0 +1,127 @@ +use super::{default_tracing, Executable}; +use crate::{ + attach::attach_dataflow, + common::{connect_to_coordinator, resolve_dataflow}, +}; +use communication_layer_request_reply::TcpRequestReplyConnection; +use dora_core::{ + descriptor::{Descriptor, DescriptorExt}, + topics::{DORA_COORDINATOR_PORT_CONTROL_DEFAULT, LOCALHOST}, +}; +use dora_message::{cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply}; +use eyre::{bail, Context}; +use std::{net::IpAddr, path::PathBuf}; +use uuid::Uuid; + +#[derive(Debug, clap::Args)] +/// Start the given dataflow path. Attach a name to the running dataflow by using --name. +pub struct Start { + /// Path to the dataflow descriptor file + #[clap(value_name = "PATH")] + dataflow: String, + /// Assign a name to the dataflow + #[clap(long)] + name: Option, + /// Address of the dora coordinator + #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] + coordinator_addr: IpAddr, + /// Port number of the coordinator control server + #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] + coordinator_port: u16, + /// Attach to the dataflow and wait for its completion + #[clap(long, action)] + attach: bool, + /// Run the dataflow in background + #[clap(long, action)] + detach: bool, + /// Enable hot reloading (Python only) + #[clap(long, action)] + hot_reload: bool, + // Use UV to run nodes. + #[clap(long, action)] + uv: bool, +} + +impl Executable for Start { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + let dataflow = resolve_dataflow(self.dataflow).context("could not resolve dataflow")?; + let dataflow_descriptor = + Descriptor::blocking_read(&dataflow).wrap_err("Failed to read yaml dataflow")?; + let working_dir = dataflow + .canonicalize() + .context("failed to canonicalize dataflow path")? + .parent() + .ok_or_else(|| eyre::eyre!("dataflow path has no parent dir"))? + .to_owned(); + + let coordinator_socket = (self.coordinator_addr, self.coordinator_port).into(); + let mut session = connect_to_coordinator(coordinator_socket) + .wrap_err("failed to connect to dora coordinator")?; + let dataflow_id = start_dataflow( + dataflow_descriptor.clone(), + self.name, + working_dir, + &mut *session, + self.uv, + )?; + + let attach = match (self.attach, self.detach) { + (true, true) => eyre::bail!("both `--attach` and `--detach` are given"), + (true, false) => true, + (false, true) => false, + (false, false) => { + println!("attaching to dataflow (use `--detach` to run in background)"); + true + } + }; + + if attach { + attach_dataflow( + dataflow_descriptor, + dataflow, + dataflow_id, + &mut *session, + self.hot_reload, + coordinator_socket, + env_logger::Builder::new() + .filter_level(log::LevelFilter::Info) + .parse_default_env() + .build() + .filter(), + )?; + } + Ok(()) + } +} + +fn start_dataflow( + dataflow: Descriptor, + name: Option, + local_working_dir: PathBuf, + session: &mut TcpRequestReplyConnection, + uv: bool, +) -> Result { + let reply_raw = session + .request( + &serde_json::to_vec(&ControlRequest::Start { + dataflow, + name, + local_working_dir, + uv, + }) + .unwrap(), + ) + .wrap_err("failed to send start dataflow message")?; + + let result: ControlRequestReply = + serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + match result { + ControlRequestReply::DataflowStarted { uuid } => { + eprintln!("{uuid}"); + Ok(uuid) + } + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected start dataflow reply: {other:?}"), + } +} diff --git a/binaries/cli/src/commands/stop.rs b/binaries/cli/src/commands/stop.rs new file mode 100644 index 00000000..01d63402 --- /dev/null +++ b/binaries/cli/src/commands/stop.rs @@ -0,0 +1,111 @@ +use super::{default_tracing, Executable}; +use crate::common::{connect_to_coordinator, handle_dataflow_result, query_running_dataflows}; +use communication_layer_request_reply::TcpRequestReplyConnection; +use dora_core::topics::{DORA_COORDINATOR_PORT_CONTROL_DEFAULT, LOCALHOST}; +use dora_message::cli_to_coordinator::ControlRequest; +use dora_message::coordinator_to_cli::ControlRequestReply; +use duration_str::parse; +use eyre::{bail, Context}; +use std::net::IpAddr; +use std::time::Duration; +use uuid::Uuid; + +#[derive(Debug, clap::Args)] +/// Stop the given dataflow UUID. If no id is provided, you will be able to choose between the running dataflows. +pub struct Stop { + /// UUID of the dataflow that should be stopped + uuid: Option, + /// Name of the dataflow that should be stopped + #[clap(long)] + name: Option, + /// Kill the dataflow if it doesn't stop after the given duration + #[clap(long, value_name = "DURATION")] + #[arg(value_parser = parse)] + grace_duration: Option, + /// Address of the dora coordinator + #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] + coordinator_addr: IpAddr, + /// Port number of the coordinator control server + #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] + coordinator_port: u16, +} + +impl Executable for Stop { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + let mut session = + connect_to_coordinator((self.coordinator_addr, self.coordinator_port).into()) + .wrap_err("could not connect to dora coordinator")?; + match (self.uuid, self.name) { + (Some(uuid), _) => stop_dataflow(uuid, self.grace_duration, &mut *session), + (None, Some(name)) => stop_dataflow_by_name(name, self.grace_duration, &mut *session), + (None, None) => stop_dataflow_interactive(self.grace_duration, &mut *session), + } + } +} + +fn stop_dataflow_interactive( + grace_duration: Option, + session: &mut TcpRequestReplyConnection, +) -> eyre::Result<()> { + let list = query_running_dataflows(session).wrap_err("failed to query running dataflows")?; + let active = list.get_active(); + if active.is_empty() { + eprintln!("No dataflows are running"); + } else { + let selection = inquire::Select::new("Choose dataflow to stop:", active).prompt()?; + stop_dataflow(selection.uuid, grace_duration, session)?; + } + + Ok(()) +} + +fn stop_dataflow( + uuid: Uuid, + grace_duration: Option, + session: &mut TcpRequestReplyConnection, +) -> Result<(), eyre::ErrReport> { + let reply_raw = session + .request( + &serde_json::to_vec(&ControlRequest::Stop { + dataflow_uuid: uuid, + grace_duration, + }) + .unwrap(), + ) + .wrap_err("failed to send dataflow stop message")?; + let result: ControlRequestReply = + serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + match result { + ControlRequestReply::DataflowStopped { uuid, result } => { + handle_dataflow_result(result, Some(uuid)) + } + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected stop dataflow reply: {other:?}"), + } +} + +fn stop_dataflow_by_name( + name: String, + grace_duration: Option, + session: &mut TcpRequestReplyConnection, +) -> Result<(), eyre::ErrReport> { + let reply_raw = session + .request( + &serde_json::to_vec(&ControlRequest::StopByName { + name, + grace_duration, + }) + .unwrap(), + ) + .wrap_err("failed to send dataflow stop_by_name message")?; + let result: ControlRequestReply = + serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + match result { + ControlRequestReply::DataflowStopped { uuid, result } => { + handle_dataflow_result(result, Some(uuid)) + } + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected stop dataflow reply: {other:?}"), + } +} diff --git a/binaries/cli/src/up.rs b/binaries/cli/src/commands/up.rs similarity index 88% rename from binaries/cli/src/up.rs rename to binaries/cli/src/commands/up.rs index 16f1a4c1..a9acbd93 100644 --- a/binaries/cli/src/up.rs +++ b/binaries/cli/src/commands/up.rs @@ -1,8 +1,27 @@ -use crate::{check::daemon_running, connect_to_coordinator, LOCALHOST}; +use super::check::daemon_running; +use super::{default_tracing, Executable}; +use crate::{common::connect_to_coordinator, LOCALHOST}; use dora_core::topics::DORA_COORDINATOR_PORT_CONTROL_DEFAULT; use dora_message::{cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply}; use eyre::{bail, Context, ContextCompat}; +use std::path::PathBuf; use std::{fs, net::SocketAddr, path::Path, process::Command, time::Duration}; + +#[derive(Debug, clap::Args)] +/// Spawn coordinator and daemon in local mode (with default config) +pub struct Up { + /// Use a custom configuration + #[clap(long, hide = true, value_name = "PATH", value_hint = clap::ValueHint::FilePath)] + config: Option, +} + +impl Executable for Up { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + up(self.config.as_deref()) + } +} + #[derive(Debug, Default, serde::Serialize, serde::Deserialize)] struct UpConfig {} diff --git a/binaries/cli/src/common.rs b/binaries/cli/src/common.rs new file mode 100644 index 00000000..2cc71564 --- /dev/null +++ b/binaries/cli/src/common.rs @@ -0,0 +1,69 @@ +use crate::formatting::FormatDataflowError; +use communication_layer_request_reply::{RequestReplyLayer, TcpLayer, TcpRequestReplyConnection}; +use dora_core::descriptor::source_is_url; +use dora_download::download_file; +use dora_message::{ + cli_to_coordinator::ControlRequest, + coordinator_to_cli::{ControlRequestReply, DataflowList, DataflowResult}, +}; +use eyre::{bail, Context}; +use std::{env::current_dir, net::SocketAddr, path::PathBuf}; +use tokio::runtime::Builder; +use uuid::Uuid; + +pub(crate) fn handle_dataflow_result( + result: DataflowResult, + uuid: Option, +) -> Result<(), eyre::Error> { + if result.is_ok() { + Ok(()) + } else { + Err(match uuid { + Some(uuid) => { + eyre::eyre!("Dataflow {uuid} failed:\n{}", FormatDataflowError(&result)) + } + None => { + eyre::eyre!("Dataflow failed:\n{}", FormatDataflowError(&result)) + } + }) + } +} + +pub(crate) fn query_running_dataflows( + session: &mut TcpRequestReplyConnection, +) -> eyre::Result { + let reply_raw = session + .request(&serde_json::to_vec(&ControlRequest::List).unwrap()) + .wrap_err("failed to send list message")?; + let reply: ControlRequestReply = + serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + let ids = match reply { + ControlRequestReply::DataflowList(list) => list, + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected list dataflow reply: {other:?}"), + }; + + Ok(ids) +} + +pub(crate) fn connect_to_coordinator( + coordinator_addr: SocketAddr, +) -> std::io::Result> { + TcpLayer::new().connect(coordinator_addr) +} + +pub(crate) fn resolve_dataflow(dataflow: String) -> eyre::Result { + let dataflow = if source_is_url(&dataflow) { + // try to download the shared library + let target_path = current_dir().context("Could not access the current dir")?; + let rt = Builder::new_current_thread() + .enable_all() + .build() + .context("tokio runtime failed")?; + rt.block_on(async { download_file(&dataflow, &target_path).await }) + .wrap_err("failed to download dataflow yaml file")? + } else { + PathBuf::from(dataflow) + }; + Ok(dataflow) +} diff --git a/binaries/cli/src/lib.rs b/binaries/cli/src/lib.rs index cfe9e41b..6575324d 100644 --- a/binaries/cli/src/lib.rs +++ b/binaries/cli/src/lib.rs @@ -1,44 +1,15 @@ -use attach::attach_dataflow; use colored::Colorize; -use communication_layer_request_reply::{RequestReplyLayer, TcpLayer, TcpRequestReplyConnection}; -use dora_coordinator::Event; -use dora_core::{ - descriptor::{source_is_url, Descriptor, DescriptorExt}, - topics::{ - DORA_COORDINATOR_PORT_CONTROL_DEFAULT, DORA_COORDINATOR_PORT_DEFAULT, - DORA_DAEMON_LOCAL_LISTEN_PORT_DEFAULT, - }, -}; -use dora_daemon::Daemon; -use dora_download::download_file; -use dora_message::{ - cli_to_coordinator::ControlRequest, - coordinator_to_cli::{ControlRequestReply, DataflowList, DataflowResult, DataflowStatus}, -}; -#[cfg(feature = "tracing")] -use dora_tracing::TracingBuilder; -use duration_str::parse; -use eyre::{bail, Context}; -use formatting::FormatDataflowError; -use std::{env::current_dir, io::Write, net::SocketAddr}; +use commands::Executable; use std::{ net::{IpAddr, Ipv4Addr}, path::PathBuf, - time::Duration, }; -use tabwriter::TabWriter; -use tokio::runtime::Builder; -use tracing::level_filters::LevelFilter; -use uuid::Uuid; mod attach; -mod build; -mod check; +mod commands; +mod common; mod formatting; -mod graph; -mod logs; mod template; -mod up; const LOCALHOST: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); const LISTEN_WILDCARD: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); @@ -47,219 +18,7 @@ const LISTEN_WILDCARD: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); #[clap(version)] pub struct Args { #[clap(subcommand)] - command: Command, -} - -/// dora-rs cli client -#[derive(Debug, clap::Subcommand)] -enum Command { - /// Check if the coordinator and the daemon is running. - Check { - /// Path to the dataflow descriptor file (enables additional checks) - #[clap(long, value_name = "PATH", value_hint = clap::ValueHint::FilePath)] - dataflow: Option, - /// Address of the dora coordinator - #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] - coordinator_addr: IpAddr, - /// Port number of the coordinator control server - #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] - coordinator_port: u16, - }, - /// Generate a visualization of the given graph using mermaid.js. Use --open to open browser. - Graph { - /// Path to the dataflow descriptor file - #[clap(value_name = "PATH", value_hint = clap::ValueHint::FilePath)] - dataflow: PathBuf, - /// Visualize the dataflow as a Mermaid diagram (instead of HTML) - #[clap(long, action)] - mermaid: bool, - /// Open the HTML visualization in the browser - #[clap(long, action)] - open: bool, - }, - /// Run build commands provided in the given dataflow. - Build { - /// Path to the dataflow descriptor file - #[clap(value_name = "PATH")] - dataflow: String, - // Use UV to build nodes. - #[clap(long, action)] - uv: bool, - }, - /// Generate a new project or node. Choose the language between Rust, Python, C or C++. - New { - #[clap(flatten)] - args: CommandNew, - #[clap(hide = true, long)] - internal_create_with_path_dependencies: bool, - }, - /// Run a dataflow locally. - /// - /// Directly runs the given dataflow without connecting to a dora - /// coordinator or daemon. The dataflow is executed on the local machine. - Run { - /// Path to the dataflow descriptor file - #[clap(value_name = "PATH")] - dataflow: String, - // Use UV to run nodes. - #[clap(long, action)] - uv: bool, - }, - /// Spawn coordinator and daemon in local mode (with default config) - Up { - /// Use a custom configuration - #[clap(long, hide = true, value_name = "PATH", value_hint = clap::ValueHint::FilePath)] - config: Option, - }, - /// Destroy running coordinator and daemon. If some dataflows are still running, they will be stopped first. - Destroy { - /// Use a custom configuration - #[clap(long, hide = true)] - config: Option, - /// Address of the dora coordinator - #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] - coordinator_addr: IpAddr, - /// Port number of the coordinator control server - #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] - coordinator_port: u16, - }, - /// Start the given dataflow path. Attach a name to the running dataflow by using --name. - Start { - /// Path to the dataflow descriptor file - #[clap(value_name = "PATH")] - dataflow: String, - /// Assign a name to the dataflow - #[clap(long)] - name: Option, - /// Address of the dora coordinator - #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] - coordinator_addr: IpAddr, - /// Port number of the coordinator control server - #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] - coordinator_port: u16, - /// Attach to the dataflow and wait for its completion - #[clap(long, action)] - attach: bool, - /// Run the dataflow in background - #[clap(long, action)] - detach: bool, - /// Enable hot reloading (Python only) - #[clap(long, action)] - hot_reload: bool, - // Use UV to run nodes. - #[clap(long, action)] - uv: bool, - }, - /// Stop the given dataflow UUID. If no id is provided, you will be able to choose between the running dataflows. - Stop { - /// UUID of the dataflow that should be stopped - uuid: Option, - /// Name of the dataflow that should be stopped - #[clap(long)] - name: Option, - /// Kill the dataflow if it doesn't stop after the given duration - #[clap(long, value_name = "DURATION")] - #[arg(value_parser = parse)] - grace_duration: Option, - /// Address of the dora coordinator - #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] - coordinator_addr: IpAddr, - /// Port number of the coordinator control server - #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] - coordinator_port: u16, - }, - /// List running dataflows. - List { - /// Address of the dora coordinator - #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] - coordinator_addr: IpAddr, - /// Port number of the coordinator control server - #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] - coordinator_port: u16, - }, - // Planned for future releases: - // Dashboard, - /// Show logs of a given dataflow and node. - #[command(allow_missing_positional = true)] - Logs { - /// Identifier of the dataflow - #[clap(value_name = "UUID_OR_NAME")] - dataflow: Option, - /// Show logs for the given node - #[clap(value_name = "NAME")] - node: String, - /// Address of the dora coordinator - #[clap(long, value_name = "IP", default_value_t = LOCALHOST)] - coordinator_addr: IpAddr, - /// Port number of the coordinator control server - #[clap(long, value_name = "PORT", default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] - coordinator_port: u16, - }, - // Metrics, - // Stats, - // Get, - // Upgrade, - /// Run daemon - Daemon { - /// Unique identifier for the machine (required for distributed dataflows) - #[clap(long)] - machine_id: Option, - /// Local listen port for event such as dynamic node. - #[clap(long, default_value_t = DORA_DAEMON_LOCAL_LISTEN_PORT_DEFAULT)] - local_listen_port: u16, - /// Address and port number of the dora coordinator - #[clap(long, short, default_value_t = LOCALHOST)] - coordinator_addr: IpAddr, - /// Port number of the coordinator control server - #[clap(long, default_value_t = DORA_COORDINATOR_PORT_DEFAULT)] - coordinator_port: u16, - #[clap(long, hide = true)] - run_dataflow: Option, - /// Suppresses all log output to stdout. - #[clap(long)] - quiet: bool, - }, - /// Run runtime - Runtime, - /// Run coordinator - Coordinator { - /// Network interface to bind to for daemon communication - #[clap(long, default_value_t = LISTEN_WILDCARD)] - interface: IpAddr, - /// Port number to bind to for daemon communication - #[clap(long, default_value_t = DORA_COORDINATOR_PORT_DEFAULT)] - port: u16, - /// Network interface to bind to for control communication - #[clap(long, default_value_t = LISTEN_WILDCARD)] - control_interface: IpAddr, - /// Port number to bind to for control communication - #[clap(long, default_value_t = DORA_COORDINATOR_PORT_CONTROL_DEFAULT)] - control_port: u16, - /// Suppresses all log output to stdout. - #[clap(long)] - quiet: bool, - }, - /// Dora CLI self-management commands - Self_ { - #[clap(subcommand)] - command: SelfSubCommand, - }, -} - -#[derive(Debug, clap::Subcommand)] -enum SelfSubCommand { - /// Check for updates or update the CLI - Update { - /// Only check for updates without installing - #[clap(long)] - check_only: bool, - }, - /// Remove The Dora CLI from the system - Uninstall { - /// Force uninstallation without confirmation - #[clap(long)] - force: bool, - }, + command: commands::Command, } #[derive(Debug, clap::Args)] @@ -292,568 +51,13 @@ enum Lang { } pub fn lib_main(args: Args) { - if let Err(err) = run(args) { + if let Err(err) = args.command.execute() { eprintln!("\n\n{}", "[ERROR]".bold().red()); eprintln!("{err:?}"); std::process::exit(1); } } -fn run(args: Args) -> eyre::Result<()> { - #[cfg(feature = "tracing")] - match &args.command { - Command::Daemon { - quiet, machine_id, .. - } => { - let name = "dora-daemon"; - let filename = machine_id - .as_ref() - .map(|id| format!("{name}-{id}")) - .unwrap_or(name.to_string()); - - let mut builder = TracingBuilder::new(name); - if !quiet { - builder = builder.with_stdout("info,zenoh=warn"); - } - builder = builder.with_file(filename, LevelFilter::INFO)?; - builder - .build() - .wrap_err("failed to set up tracing subscriber")?; - } - Command::Runtime => { - // Do not set the runtime in the cli. - } - Command::Coordinator { quiet, .. } => { - let name = "dora-coordinator"; - let mut builder = TracingBuilder::new(name); - if !quiet { - builder = builder.with_stdout("info"); - } - builder = builder.with_file(name, LevelFilter::INFO)?; - builder - .build() - .wrap_err("failed to set up tracing subscriber")?; - } - Command::Run { .. } => { - let log_level = std::env::var("RUST_LOG").ok().unwrap_or("info".to_string()); - TracingBuilder::new("run") - .with_stdout(log_level) - .build() - .wrap_err("failed to set up tracing subscriber")?; - } - _ => { - TracingBuilder::new("dora-cli") - .with_stdout("warn") - .build() - .wrap_err("failed to set up tracing subscriber")?; - } - }; - - let log_level = env_logger::Builder::new() - .filter_level(log::LevelFilter::Info) - .parse_default_env() - .build() - .filter(); - - match args.command { - Command::Check { - dataflow, - coordinator_addr, - coordinator_port, - } => match dataflow { - Some(dataflow) => { - let working_dir = dataflow - .canonicalize() - .context("failed to canonicalize dataflow path")? - .parent() - .ok_or_else(|| eyre::eyre!("dataflow path has no parent dir"))? - .to_owned(); - Descriptor::blocking_read(&dataflow)?.check(&working_dir)?; - check::check_environment((coordinator_addr, coordinator_port).into())? - } - None => check::check_environment((coordinator_addr, coordinator_port).into())?, - }, - Command::Graph { - dataflow, - mermaid, - open, - } => { - graph::create(dataflow, mermaid, open)?; - } - Command::Build { dataflow, uv } => { - build::build(dataflow, uv)?; - } - Command::New { - args, - internal_create_with_path_dependencies, - } => template::create(args, internal_create_with_path_dependencies)?, - Command::Run { dataflow, uv } => { - let dataflow_path = resolve_dataflow(dataflow).context("could not resolve dataflow")?; - let rt = Builder::new_multi_thread() - .enable_all() - .build() - .context("tokio runtime failed")?; - let result = rt.block_on(Daemon::run_dataflow(&dataflow_path, uv))?; - handle_dataflow_result(result, None)? - } - Command::Up { config } => { - up::up(config.as_deref())?; - } - Command::Logs { - dataflow, - node, - coordinator_addr, - coordinator_port, - } => { - let mut session = connect_to_coordinator((coordinator_addr, coordinator_port).into()) - .wrap_err("failed to connect to dora coordinator")?; - let list = query_running_dataflows(&mut *session) - .wrap_err("failed to query running dataflows")?; - if let Some(dataflow) = dataflow { - let uuid = Uuid::parse_str(&dataflow).ok(); - let name = if uuid.is_some() { None } else { Some(dataflow) }; - logs::logs(&mut *session, uuid, name, node)? - } else { - let active = list.get_active(); - let uuid = match &active[..] { - [] => bail!("No dataflows are running"), - [uuid] => uuid.clone(), - _ => inquire::Select::new("Choose dataflow to show logs:", active).prompt()?, - }; - logs::logs(&mut *session, Some(uuid.uuid), None, node)? - } - } - Command::Start { - dataflow, - name, - coordinator_addr, - coordinator_port, - attach, - detach, - hot_reload, - uv, - } => { - let dataflow = resolve_dataflow(dataflow).context("could not resolve dataflow")?; - let dataflow_descriptor = - Descriptor::blocking_read(&dataflow).wrap_err("Failed to read yaml dataflow")?; - let working_dir = dataflow - .canonicalize() - .context("failed to canonicalize dataflow path")? - .parent() - .ok_or_else(|| eyre::eyre!("dataflow path has no parent dir"))? - .to_owned(); - - let coordinator_socket = (coordinator_addr, coordinator_port).into(); - let mut session = connect_to_coordinator(coordinator_socket) - .wrap_err("failed to connect to dora coordinator")?; - let dataflow_id = start_dataflow( - dataflow_descriptor.clone(), - name, - working_dir, - &mut *session, - uv, - )?; - - let attach = match (attach, detach) { - (true, true) => eyre::bail!("both `--attach` and `--detach` are given"), - (true, false) => true, - (false, true) => false, - (false, false) => { - println!("attaching to dataflow (use `--detach` to run in background)"); - true - } - }; - - if attach { - attach_dataflow( - dataflow_descriptor, - dataflow, - dataflow_id, - &mut *session, - hot_reload, - coordinator_socket, - log_level, - )? - } - } - Command::List { - coordinator_addr, - coordinator_port, - } => match connect_to_coordinator((coordinator_addr, coordinator_port).into()) { - Ok(mut session) => list(&mut *session)?, - Err(_) => { - bail!("No dora coordinator seems to be running."); - } - }, - Command::Stop { - uuid, - name, - grace_duration, - coordinator_addr, - coordinator_port, - } => { - let mut session = connect_to_coordinator((coordinator_addr, coordinator_port).into()) - .wrap_err("could not connect to dora coordinator")?; - match (uuid, name) { - (Some(uuid), _) => stop_dataflow(uuid, grace_duration, &mut *session)?, - (None, Some(name)) => stop_dataflow_by_name(name, grace_duration, &mut *session)?, - (None, None) => stop_dataflow_interactive(grace_duration, &mut *session)?, - } - } - Command::Destroy { - config, - coordinator_addr, - coordinator_port, - } => up::destroy( - config.as_deref(), - (coordinator_addr, coordinator_port).into(), - )?, - Command::Coordinator { - interface, - port, - control_interface, - control_port, - quiet, - } => { - let rt = Builder::new_multi_thread() - .enable_all() - .build() - .context("tokio runtime failed")?; - rt.block_on(async { - let bind = SocketAddr::new(interface, port); - let bind_control = SocketAddr::new(control_interface, control_port); - let (port, task) = - dora_coordinator::start(bind, bind_control, futures::stream::empty::()) - .await?; - if !quiet { - println!("Listening for incoming daemon connection on {port}"); - } - task.await - }) - .context("failed to run dora-coordinator")? - } - Command::Daemon { - coordinator_addr, - coordinator_port, - local_listen_port, - machine_id, - run_dataflow, - quiet: _, - } => { - let rt = Builder::new_multi_thread() - .enable_all() - .build() - .context("tokio runtime failed")?; - rt.block_on(async { - match run_dataflow { - Some(dataflow_path) => { - tracing::info!("Starting dataflow `{}`", dataflow_path.display()); - if coordinator_addr != LOCALHOST { - tracing::info!( - "Not using coordinator addr {} as `run_dataflow` is for local dataflow only. Please use the `start` command for remote coordinator", - coordinator_addr - ); - } - - let result = Daemon::run_dataflow(&dataflow_path, false).await?; - handle_dataflow_result(result, None) - } - None => { - Daemon::run(SocketAddr::new(coordinator_addr, coordinator_port), machine_id, local_listen_port).await - } - } - }) - .context("failed to run dora-daemon")? - } - Command::Runtime => dora_runtime::main().context("Failed to run dora-runtime")?, - Command::Self_ { command } => match command { - SelfSubCommand::Update { check_only } => { - println!("Checking for updates..."); - - #[cfg(target_os = "linux")] - let bin_path_in_archive = format!("dora-cli-{}/dora", env!("TARGET")); - #[cfg(target_os = "macos")] - let bin_path_in_archive = format!("dora-cli-{}/dora", env!("TARGET")); - #[cfg(target_os = "windows")] - let bin_path_in_archive = String::from("dora.exe"); - - let status = self_update::backends::github::Update::configure() - .repo_owner("dora-rs") - .repo_name("dora") - .bin_path_in_archive(&bin_path_in_archive) - .bin_name("dora") - .show_download_progress(true) - .current_version(env!("CARGO_PKG_VERSION")) - .build()?; - - if check_only { - // Only check if an update is available - match status.get_latest_release() { - Ok(release) => { - let current_version = self_update::cargo_crate_version!(); - if current_version != release.version { - println!( - "An update is available: {}. Run 'dora self update' to update", - release.version - ); - } else { - println!( - "Dora CLI is already at the latest version: {}", - current_version - ); - } - } - Err(e) => println!("Failed to check for updates: {}", e), - } - } else { - // Perform the actual update - match status.update() { - Ok(update_status) => match update_status { - self_update::Status::UpToDate(version) => { - println!("Dora CLI is already at the latest version: {}", version); - } - self_update::Status::Updated(version) => { - println!("Successfully updated Dora CLI to version: {}", version); - } - }, - Err(e) => println!("Failed to update: {}", e), - } - } - } - SelfSubCommand::Uninstall { force } => { - if !force { - let confirmed = - inquire::Confirm::new("Are you sure you want to uninstall Dora CLI?") - .with_default(false) - .prompt() - .wrap_err("Uninstallation cancelled")?; - - if !confirmed { - println!("Uninstallation cancelled"); - return Ok(()); - } - } - - println!("Uninstalling Dora CLI..."); - #[cfg(feature = "python")] - { - println!("Detected Python installation..."); - - // Try uv pip uninstall first - let uv_status = std::process::Command::new("uv") - .args(["pip", "uninstall", "dora-rs-cli"]) - .status(); - - if let Ok(status) = uv_status { - if status.success() { - println!("Dora CLI has been successfully uninstalled via uv pip."); - return Ok(()); - } - } - - // Fall back to regular pip uninstall - println!("Trying with pip..."); - let status = std::process::Command::new("pip") - .args(["uninstall", "-y", "dora-rs-cli"]) - .status() - .wrap_err("Failed to run pip uninstall")?; - - if status.success() { - println!("Dora CLI has been successfully uninstalled via pip."); - } else { - bail!("Failed to uninstall Dora CLI via pip."); - } - } - #[cfg(not(feature = "python"))] - { - match self_replace::self_delete() { - Ok(_) => { - println!("Dora CLI has been successfully uninstalled."); - } - Err(e) => { - bail!("Failed to uninstall Dora CLI: {}", e); - } - } - } - } - }, - }; - - Ok(()) -} - -fn start_dataflow( - dataflow: Descriptor, - name: Option, - local_working_dir: PathBuf, - session: &mut TcpRequestReplyConnection, - uv: bool, -) -> Result { - let reply_raw = session - .request( - &serde_json::to_vec(&ControlRequest::Start { - dataflow, - name, - local_working_dir, - uv, - }) - .unwrap(), - ) - .wrap_err("failed to send start dataflow message")?; - - let result: ControlRequestReply = - serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; - match result { - ControlRequestReply::DataflowStarted { uuid } => { - eprintln!("{uuid}"); - Ok(uuid) - } - ControlRequestReply::Error(err) => bail!("{err}"), - other => bail!("unexpected start dataflow reply: {other:?}"), - } -} - -fn stop_dataflow_interactive( - grace_duration: Option, - session: &mut TcpRequestReplyConnection, -) -> eyre::Result<()> { - let list = query_running_dataflows(session).wrap_err("failed to query running dataflows")?; - let active = list.get_active(); - if active.is_empty() { - eprintln!("No dataflows are running"); - } else { - let selection = inquire::Select::new("Choose dataflow to stop:", active).prompt()?; - stop_dataflow(selection.uuid, grace_duration, session)?; - } - - Ok(()) -} - -fn stop_dataflow( - uuid: Uuid, - grace_duration: Option, - session: &mut TcpRequestReplyConnection, -) -> Result<(), eyre::ErrReport> { - let reply_raw = session - .request( - &serde_json::to_vec(&ControlRequest::Stop { - dataflow_uuid: uuid, - grace_duration, - }) - .unwrap(), - ) - .wrap_err("failed to send dataflow stop message")?; - let result: ControlRequestReply = - serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; - match result { - ControlRequestReply::DataflowStopped { uuid, result } => { - handle_dataflow_result(result, Some(uuid)) - } - ControlRequestReply::Error(err) => bail!("{err}"), - other => bail!("unexpected stop dataflow reply: {other:?}"), - } -} - -fn handle_dataflow_result(result: DataflowResult, uuid: Option) -> Result<(), eyre::Error> { - if result.is_ok() { - Ok(()) - } else { - Err(match uuid { - Some(uuid) => { - eyre::eyre!("Dataflow {uuid} failed:\n{}", FormatDataflowError(&result)) - } - None => { - eyre::eyre!("Dataflow failed:\n{}", FormatDataflowError(&result)) - } - }) - } -} - -fn stop_dataflow_by_name( - name: String, - grace_duration: Option, - session: &mut TcpRequestReplyConnection, -) -> Result<(), eyre::ErrReport> { - let reply_raw = session - .request( - &serde_json::to_vec(&ControlRequest::StopByName { - name, - grace_duration, - }) - .unwrap(), - ) - .wrap_err("failed to send dataflow stop_by_name message")?; - let result: ControlRequestReply = - serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; - match result { - ControlRequestReply::DataflowStopped { uuid, result } => { - handle_dataflow_result(result, Some(uuid)) - } - ControlRequestReply::Error(err) => bail!("{err}"), - other => bail!("unexpected stop dataflow reply: {other:?}"), - } -} - -fn list(session: &mut TcpRequestReplyConnection) -> Result<(), eyre::ErrReport> { - let list = query_running_dataflows(session)?; - - let mut tw = TabWriter::new(vec![]); - tw.write_all(b"UUID\tName\tStatus\n")?; - for entry in list.0 { - let uuid = entry.id.uuid; - let name = entry.id.name.unwrap_or_default(); - let status = match entry.status { - DataflowStatus::Running => "Running", - DataflowStatus::Finished => "Succeeded", - DataflowStatus::Failed => "Failed", - }; - tw.write_all(format!("{uuid}\t{name}\t{status}\n").as_bytes())?; - } - tw.flush()?; - let formatted = String::from_utf8(tw.into_inner()?)?; - - println!("{formatted}"); - - Ok(()) -} - -fn query_running_dataflows(session: &mut TcpRequestReplyConnection) -> eyre::Result { - let reply_raw = session - .request(&serde_json::to_vec(&ControlRequest::List).unwrap()) - .wrap_err("failed to send list message")?; - let reply: ControlRequestReply = - serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; - let ids = match reply { - ControlRequestReply::DataflowList(list) => list, - ControlRequestReply::Error(err) => bail!("{err}"), - other => bail!("unexpected list dataflow reply: {other:?}"), - }; - - Ok(ids) -} - -fn connect_to_coordinator( - coordinator_addr: SocketAddr, -) -> std::io::Result> { - TcpLayer::new().connect(coordinator_addr) -} - -fn resolve_dataflow(dataflow: String) -> eyre::Result { - let dataflow = if source_is_url(&dataflow) { - // try to download the shared library - let target_path = current_dir().context("Could not access the current dir")?; - let rt = Builder::new_current_thread() - .enable_all() - .build() - .context("tokio runtime failed")?; - rt.block_on(async { download_file(&dataflow, &target_path).await }) - .wrap_err("failed to download dataflow yaml file")? - } else { - PathBuf::from(dataflow) - }; - Ok(dataflow) -} - #[cfg(feature = "python")] use clap::Parser; #[cfg(feature = "python")] diff --git a/binaries/cli/src/logs.rs b/binaries/cli/src/logs.rs deleted file mode 100644 index 027f3793..00000000 --- a/binaries/cli/src/logs.rs +++ /dev/null @@ -1,45 +0,0 @@ -use communication_layer_request_reply::TcpRequestReplyConnection; -use dora_message::{cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply}; -use eyre::{bail, Context, Result}; -use uuid::Uuid; - -use bat::{Input, PrettyPrinter}; - -pub fn logs( - session: &mut TcpRequestReplyConnection, - uuid: Option, - name: Option, - node: String, -) -> Result<()> { - let logs = { - let reply_raw = session - .request( - &serde_json::to_vec(&ControlRequest::Logs { - uuid, - name, - node: node.clone(), - }) - .wrap_err("")?, - ) - .wrap_err("failed to send Logs request message")?; - - let reply = serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; - match reply { - ControlRequestReply::Logs(logs) => logs, - other => bail!("unexpected reply to daemon logs: {other:?}"), - } - }; - - PrettyPrinter::new() - .header(false) - .grid(false) - .line_numbers(false) - .paging_mode(bat::PagingMode::QuitIfOneScreen) - .inputs(vec![Input::from_bytes(&logs) - .name("Logs") - .title(format!("Logs from {node}.").as_str())]) - .print() - .wrap_err("Something went wrong with viewing log file")?; - - Ok(()) -} From 2cef9eb6261d862dfe02a07f78f64451e71ca43c Mon Sep 17 00:00:00 2001 From: haixuantao Date: Wed, 25 Jun 2025 15:08:16 +0200 Subject: [PATCH 05/40] Adding monochrome encoding and examples --- Cargo.lock | 4 +- examples/vggt/depth-to-avif.yaml | 54 +++++++++++++++++++++++++++ examples/vggt/depth.dora-session.yaml | 8 ---- examples/vggt/image_saver.py | 34 +++++++++++++++++ node-hub/dora-rav1e/Cargo.toml | 2 +- node-hub/dora-rav1e/src/lib.rs | 25 ++++++++++++- node-hub/dora-vggt/dora_vggt/main.py | 30 +++++++++------ 7 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 examples/vggt/depth-to-avif.yaml delete mode 100644 examples/vggt/depth.dora-session.yaml create mode 100644 examples/vggt/image_saver.py diff --git a/Cargo.lock b/Cargo.lock index e3bf55bf..5cd2486e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1165,9 +1165,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +checksum = "19135c0c7a60bfee564dbe44ab5ce0557c6bf3884e5291a50be76a15640c4fbd" dependencies = [ "arrayvec", ] diff --git a/examples/vggt/depth-to-avif.yaml b/examples/vggt/depth-to-avif.yaml new file mode 100644 index 00000000..6db92ac3 --- /dev/null +++ b/examples/vggt/depth-to-avif.yaml @@ -0,0 +1,54 @@ +nodes: + - id: camera + build: pip install opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 1 + + - id: dora-vggt + build: pip install -e ../../node-hub/dora-vggt + path: dora-vggt + inputs: + image: camera/image + outputs: + - depth + - image + env: + DEPTH_ENCODING: mono16 + + - id: rav1e-depth + path: dora-rav1e + build: cargo build -p dora-rav1e --release + inputs: + depth: dora-vggt/depth + outputs: + - depth + env: + ENCODING: avif + + - id: rav1e-image + path: dora-rav1e + build: cargo build -p dora-rav1e --release + inputs: + image: dora-vggt/image + outputs: + - image + env: + ENCODING: avif + + - id: bench + path: image_saver.py + inputs: + camera_depth: rav1e-image/image + vggt_depth: rav1e-depth/depth + + - id: plot + build: pip install dora-rerun + path: dora-rerun + inputs: + camera/image: dora-vggt/image + camera/depth: dora-vggt/depth diff --git a/examples/vggt/depth.dora-session.yaml b/examples/vggt/depth.dora-session.yaml deleted file mode 100644 index 13428f1b..00000000 --- a/examples/vggt/depth.dora-session.yaml +++ /dev/null @@ -1,8 +0,0 @@ -build_id: 2b402c1e-e52e-45e9-86e5-236b33a77369 -session_id: 275de19c-e605-4865-bc5f-2f15916bade9 -git_sources: {} -local_build: - node_working_dirs: - camera: /Users/xaviertao/Documents/work/dora/examples/vggt - dora-vggt: /Users/xaviertao/Documents/work/dora/examples/vggt - plot: /Users/xaviertao/Documents/work/dora/examples/vggt diff --git a/examples/vggt/image_saver.py b/examples/vggt/image_saver.py new file mode 100644 index 00000000..5552d3ba --- /dev/null +++ b/examples/vggt/image_saver.py @@ -0,0 +1,34 @@ +from dora import Node + +node = Node() + +index_dict = {} +i = 0 + +LEAD_TOPIC = "vggt_depth" + +for event in node: + if event["type"] == "INPUT": + if LEAD_TOPIC in event["id"]: + storage = event["value"] + metadata = event["metadata"] + encoding = metadata["encoding"] + width = metadata["width"] + height = metadata["height"] + + # Save to file + filename = f"out/{event['id']}_{i}.{encoding}" + with open(filename, "wb") as f: + f.write(storage.to_numpy()) + for key, value in index_dict.items(): + filename = f"out/{key}_{i}.{value['metadata']['encoding']}" + with open(filename, "wb") as f: + f.write(value["value"]) + i += 1 + else: + # Store the event in the index dictionary + index_dict[event["id"]] = { + "type": event["type"], + "value": event["value"].to_numpy(), + "metadata": event["metadata"], + } diff --git a/node-hub/dora-rav1e/Cargo.toml b/node-hub/dora-rav1e/Cargo.toml index c2e35cd2..5dcb6b73 100644 --- a/node-hub/dora-rav1e/Cargo.toml +++ b/node-hub/dora-rav1e/Cargo.toml @@ -25,7 +25,7 @@ pyo3 = { workspace = true, features = [ "eyre", "generate-import-lib", ], optional = true } -avif-serialize = "0.8.3" +avif-serialize = "0.8.4" [lib] diff --git a/node-hub/dora-rav1e/src/lib.rs b/node-hub/dora-rav1e/src/lib.rs index 22e43180..68280155 100644 --- a/node-hub/dora-rav1e/src/lib.rs +++ b/node-hub/dora-rav1e/src/lib.rs @@ -336,7 +336,7 @@ pub fn lib_main() -> Result<()> { if let Some(buffer) = data.as_primitive_opt::() { let mut buffer = buffer.values().to_vec(); if std::env::var("FILL_ZEROS") - .map(|s| s != "false") + .map(|s| s.to_lowercase() != "false") .unwrap_or(true) { fill_zeros_toward_center_y_plane_in_place(&mut buffer, width, height); @@ -370,7 +370,28 @@ pub fn lib_main() -> Result<()> { let data = pkt.data; match output_encoding.as_str() { "avif" => { - warn!("avif encoding not supported for mono16"); + metadata.parameters.insert( + "encoding".to_string(), + Parameter::String("avif".to_string()), + ); + + let data = avif_serialize::Aviffy::new() + .full_color_range(false) + .set_seq_profile(0) + .set_monochrome(true) + .to_vec( + &data, + None, + enc.width as u32, + enc.height as u32, + enc.bit_depth as u8, + ); + + let arrow = data.into_arrow(); + + node.send_output(id, metadata.parameters.clone(), arrow) + .context("could not send output") + .unwrap(); } _ => { metadata.parameters.insert( diff --git a/node-hub/dora-vggt/dora_vggt/main.py b/node-hub/dora-vggt/dora_vggt/main.py index 7c0e24c7..500e665d 100644 --- a/node-hub/dora-vggt/dora_vggt/main.py +++ b/node-hub/dora-vggt/dora_vggt/main.py @@ -1,6 +1,7 @@ """TODO: Add docstring.""" import io +import os from collections import deque as Deque import cv2 @@ -17,11 +18,15 @@ from vggt.utils.pose_enc import pose_encoding_to_extri_intri dtype = torch.bfloat16 +# Check if cuda is available and set the device accordingly +device = "cuda" if torch.cuda.is_available() else "cpu" + # Initialize the model and load the pretrained weights. # This will automatically download the model weights the first time it's run, which may take a while. -model = VGGT.from_pretrained("facebook/VGGT-1B").to("cuda") +model = VGGT.from_pretrained("facebook/VGGT-1B").to(device) model.eval() +DEPTH_ENCODING = os.environ.get("DEPTH_ENCODING", "float64") # Import vecdeque @@ -32,7 +37,6 @@ def main(): for event in node: if event["type"] == "INPUT": - if "image" in event["id"]: storage = event["value"] metadata = event["metadata"] @@ -80,7 +84,7 @@ def main(): raw_images.append(buffer) with torch.no_grad(): - images = load_and_preprocess_images(raw_images).to("cuda") + images = load_and_preprocess_images(raw_images).to(device) images = images[None] # add batch dimension aggregated_tokens_list, ps_idx = model.aggregator(images) @@ -107,20 +111,24 @@ def main(): depth_map = depth_map[-1][-1].cpu().numpy() # Warning: Make sure to add my_output_id and my_input_id within the dataflow. + if DEPTH_ENCODING == "mono16": + depth_map = (depth_map * 1000).astype(np.uint16) + node.send_output( output_id="depth", data=pa.array(depth_map.ravel()), metadata={ "width": depth_map.shape[1], "height": depth_map.shape[0], - "focal": [ - int(f_0), - int(f_1), - ], - "resolution": [ - int(r_0), - int(r_1), - ], + "encoding": DEPTH_ENCODING, + "focal": [ + int(f_0), + int(f_1), + ], + "resolution": [ + int(r_0), + int(r_1), + ], }, ) From 1530fe54dda0bea5053ec1d3a4dca64279f77529 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 25 Jun 2025 15:39:43 +0200 Subject: [PATCH 06/40] Rename `commands` module to `command` --- binaries/cli/src/{commands => command}/build.rs | 0 binaries/cli/src/{commands => command}/check.rs | 0 binaries/cli/src/{commands => command}/coordinator.rs | 0 binaries/cli/src/{commands => command}/daemon.rs | 0 binaries/cli/src/{commands => command}/destroy.rs | 0 binaries/cli/src/{commands => command}/graph.rs | 0 binaries/cli/src/{commands => command}/graph/.gitignore | 0 .../src/{commands => command}/graph/mermaid-template.html | 0 binaries/cli/src/{commands => command}/list.rs | 0 binaries/cli/src/{commands => command}/logs.rs | 0 binaries/cli/src/{commands => command}/new.rs | 0 binaries/cli/src/{commands => command}/run.rs | 0 binaries/cli/src/{commands => command}/runtime.rs | 0 binaries/cli/src/{commands => command}/self_.rs | 0 binaries/cli/src/{commands => command}/start/attach.rs | 0 binaries/cli/src/{commands => command}/start/mod.rs | 0 binaries/cli/src/{commands => command}/stop.rs | 0 binaries/cli/src/{commands => command}/up.rs | 0 binaries/cli/src/lib.rs | 6 +++--- 19 files changed, 3 insertions(+), 3 deletions(-) rename binaries/cli/src/{commands => command}/build.rs (100%) rename binaries/cli/src/{commands => command}/check.rs (100%) rename binaries/cli/src/{commands => command}/coordinator.rs (100%) rename binaries/cli/src/{commands => command}/daemon.rs (100%) rename binaries/cli/src/{commands => command}/destroy.rs (100%) rename binaries/cli/src/{commands => command}/graph.rs (100%) rename binaries/cli/src/{commands => command}/graph/.gitignore (100%) rename binaries/cli/src/{commands => command}/graph/mermaid-template.html (100%) rename binaries/cli/src/{commands => command}/list.rs (100%) rename binaries/cli/src/{commands => command}/logs.rs (100%) rename binaries/cli/src/{commands => command}/new.rs (100%) rename binaries/cli/src/{commands => command}/run.rs (100%) rename binaries/cli/src/{commands => command}/runtime.rs (100%) rename binaries/cli/src/{commands => command}/self_.rs (100%) rename binaries/cli/src/{commands => command}/start/attach.rs (100%) rename binaries/cli/src/{commands => command}/start/mod.rs (100%) rename binaries/cli/src/{commands => command}/stop.rs (100%) rename binaries/cli/src/{commands => command}/up.rs (100%) diff --git a/binaries/cli/src/commands/build.rs b/binaries/cli/src/command/build.rs similarity index 100% rename from binaries/cli/src/commands/build.rs rename to binaries/cli/src/command/build.rs diff --git a/binaries/cli/src/commands/check.rs b/binaries/cli/src/command/check.rs similarity index 100% rename from binaries/cli/src/commands/check.rs rename to binaries/cli/src/command/check.rs diff --git a/binaries/cli/src/commands/coordinator.rs b/binaries/cli/src/command/coordinator.rs similarity index 100% rename from binaries/cli/src/commands/coordinator.rs rename to binaries/cli/src/command/coordinator.rs diff --git a/binaries/cli/src/commands/daemon.rs b/binaries/cli/src/command/daemon.rs similarity index 100% rename from binaries/cli/src/commands/daemon.rs rename to binaries/cli/src/command/daemon.rs diff --git a/binaries/cli/src/commands/destroy.rs b/binaries/cli/src/command/destroy.rs similarity index 100% rename from binaries/cli/src/commands/destroy.rs rename to binaries/cli/src/command/destroy.rs diff --git a/binaries/cli/src/commands/graph.rs b/binaries/cli/src/command/graph.rs similarity index 100% rename from binaries/cli/src/commands/graph.rs rename to binaries/cli/src/command/graph.rs diff --git a/binaries/cli/src/commands/graph/.gitignore b/binaries/cli/src/command/graph/.gitignore similarity index 100% rename from binaries/cli/src/commands/graph/.gitignore rename to binaries/cli/src/command/graph/.gitignore diff --git a/binaries/cli/src/commands/graph/mermaid-template.html b/binaries/cli/src/command/graph/mermaid-template.html similarity index 100% rename from binaries/cli/src/commands/graph/mermaid-template.html rename to binaries/cli/src/command/graph/mermaid-template.html diff --git a/binaries/cli/src/commands/list.rs b/binaries/cli/src/command/list.rs similarity index 100% rename from binaries/cli/src/commands/list.rs rename to binaries/cli/src/command/list.rs diff --git a/binaries/cli/src/commands/logs.rs b/binaries/cli/src/command/logs.rs similarity index 100% rename from binaries/cli/src/commands/logs.rs rename to binaries/cli/src/command/logs.rs diff --git a/binaries/cli/src/commands/new.rs b/binaries/cli/src/command/new.rs similarity index 100% rename from binaries/cli/src/commands/new.rs rename to binaries/cli/src/command/new.rs diff --git a/binaries/cli/src/commands/run.rs b/binaries/cli/src/command/run.rs similarity index 100% rename from binaries/cli/src/commands/run.rs rename to binaries/cli/src/command/run.rs diff --git a/binaries/cli/src/commands/runtime.rs b/binaries/cli/src/command/runtime.rs similarity index 100% rename from binaries/cli/src/commands/runtime.rs rename to binaries/cli/src/command/runtime.rs diff --git a/binaries/cli/src/commands/self_.rs b/binaries/cli/src/command/self_.rs similarity index 100% rename from binaries/cli/src/commands/self_.rs rename to binaries/cli/src/command/self_.rs diff --git a/binaries/cli/src/commands/start/attach.rs b/binaries/cli/src/command/start/attach.rs similarity index 100% rename from binaries/cli/src/commands/start/attach.rs rename to binaries/cli/src/command/start/attach.rs diff --git a/binaries/cli/src/commands/start/mod.rs b/binaries/cli/src/command/start/mod.rs similarity index 100% rename from binaries/cli/src/commands/start/mod.rs rename to binaries/cli/src/command/start/mod.rs diff --git a/binaries/cli/src/commands/stop.rs b/binaries/cli/src/command/stop.rs similarity index 100% rename from binaries/cli/src/commands/stop.rs rename to binaries/cli/src/command/stop.rs diff --git a/binaries/cli/src/commands/up.rs b/binaries/cli/src/command/up.rs similarity index 100% rename from binaries/cli/src/commands/up.rs rename to binaries/cli/src/command/up.rs diff --git a/binaries/cli/src/lib.rs b/binaries/cli/src/lib.rs index d1e026c8..03474839 100644 --- a/binaries/cli/src/lib.rs +++ b/binaries/cli/src/lib.rs @@ -1,11 +1,11 @@ use colored::Colorize; -use commands::Executable; +use command::Executable; use std::{ net::{IpAddr, Ipv4Addr}, path::PathBuf, }; -mod commands; +mod command; mod common; mod formatting; pub mod output; @@ -19,7 +19,7 @@ const LISTEN_WILDCARD: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); #[clap(version)] pub struct Args { #[clap(subcommand)] - command: commands::Command, + command: command::Command, } #[derive(Debug, clap::Args)] From 0cc493717457f766d426fb67af10a9d632ea90e3 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 25 Jun 2025 15:57:10 +0200 Subject: [PATCH 07/40] Move and merge `command` module file --- .../cli/src/{commands.rs => command/mod.rs} | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) rename binaries/cli/src/{commands.rs => command/mod.rs} (70%) diff --git a/binaries/cli/src/commands.rs b/binaries/cli/src/command/mod.rs similarity index 70% rename from binaries/cli/src/commands.rs rename to binaries/cli/src/command/mod.rs index 7ab3804a..168e4284 100644 --- a/binaries/cli/src/commands.rs +++ b/binaries/cli/src/command/mod.rs @@ -14,12 +14,16 @@ mod start; mod stop; mod up; +use std::path::{Path, PathBuf}; + use build::Build; use check::Check; +use communication_layer_request_reply::TcpRequestReplyConnection; use coordinator::Coordinator; use daemon::Daemon; use destroy::Destroy; -use eyre::Context; +use dora_core::descriptor::Descriptor; +use eyre::{Context, ContextCompat}; use graph::Graph; use list::ListArgs; use logs::LogsArgs; @@ -31,6 +35,8 @@ use start::Start; use stop::Stop; use up::Up; +use crate::common::cli_and_daemon_on_same_machine; + /// dora-rs cli client #[derive(Debug, clap::Subcommand)] pub enum Command { @@ -100,3 +106,28 @@ impl Executable for Command { } } } + +fn local_working_dir( + dataflow_path: &Path, + dataflow_descriptor: &Descriptor, + coordinator_session: &mut TcpRequestReplyConnection, +) -> eyre::Result> { + Ok( + if dataflow_descriptor + .nodes + .iter() + .all(|n| n.deploy.as_ref().map(|d| d.machine.as_ref()).is_none()) + && cli_and_daemon_on_same_machine(coordinator_session)? + { + Some( + dunce::canonicalize(dataflow_path) + .context("failed to canonicalize dataflow file path")? + .parent() + .context("dataflow path has no parent dir")? + .to_owned(), + ) + } else { + None + }, + ) +} From b2c0f68d8c43503ff8a496e615c4070247e3cfb3 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 25 Jun 2025 15:57:36 +0200 Subject: [PATCH 08/40] Fix import --- binaries/cli/src/command/start/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binaries/cli/src/command/start/mod.rs b/binaries/cli/src/command/start/mod.rs index bdc9f6d0..455fb756 100644 --- a/binaries/cli/src/command/start/mod.rs +++ b/binaries/cli/src/command/start/mod.rs @@ -1,6 +1,6 @@ use super::{default_tracing, Executable}; use crate::{ - commands::start::attach::attach_dataflow, + command::start::attach::attach_dataflow, common::{connect_to_coordinator, local_working_dir, resolve_dataflow}, output::print_log_message, session::DataflowSession, From 1d7b9f97dc87cb9724cfb2edbc1ffc095dd29b60 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 25 Jun 2025 16:14:39 +0200 Subject: [PATCH 09/40] Remove unused function --- binaries/cli/src/command/mod.rs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/binaries/cli/src/command/mod.rs b/binaries/cli/src/command/mod.rs index 168e4284..2004b2a4 100644 --- a/binaries/cli/src/command/mod.rs +++ b/binaries/cli/src/command/mod.rs @@ -106,28 +106,3 @@ impl Executable for Command { } } } - -fn local_working_dir( - dataflow_path: &Path, - dataflow_descriptor: &Descriptor, - coordinator_session: &mut TcpRequestReplyConnection, -) -> eyre::Result> { - Ok( - if dataflow_descriptor - .nodes - .iter() - .all(|n| n.deploy.as_ref().map(|d| d.machine.as_ref()).is_none()) - && cli_and_daemon_on_same_machine(coordinator_session)? - { - Some( - dunce::canonicalize(dataflow_path) - .context("failed to canonicalize dataflow file path")? - .parent() - .context("dataflow path has no parent dir")? - .to_owned(), - ) - } else { - None - }, - ) -} From c05d4cd9ff83f6e16fc51dfadba7151fc044b625 Mon Sep 17 00:00:00 2001 From: sjfhsjfh Date: Wed, 25 Jun 2025 23:06:42 +0800 Subject: [PATCH 10/40] chore: revert deletions --- binaries/cli/src/command/build.rs | 134 ------------ binaries/cli/src/command/build/distributed.rs | 107 +++++++++ binaries/cli/src/command/build/git.rs | 45 ++++ binaries/cli/src/command/build/local.rs | 121 +++++++++++ binaries/cli/src/command/build/mod.rs | 205 ++++++++++++++++++ 5 files changed, 478 insertions(+), 134 deletions(-) delete mode 100644 binaries/cli/src/command/build.rs create mode 100644 binaries/cli/src/command/build/distributed.rs create mode 100644 binaries/cli/src/command/build/git.rs create mode 100644 binaries/cli/src/command/build/local.rs create mode 100644 binaries/cli/src/command/build/mod.rs diff --git a/binaries/cli/src/command/build.rs b/binaries/cli/src/command/build.rs deleted file mode 100644 index d781142e..00000000 --- a/binaries/cli/src/command/build.rs +++ /dev/null @@ -1,134 +0,0 @@ -use super::{default_tracing, Executable}; -use crate::common::resolve_dataflow; -use dora_core::{ - config::OperatorId, - descriptor::{Descriptor, DescriptorExt, NodeExt, SINGLE_OPERATOR_DEFAULT_ID}, -}; -use dora_message::descriptor::EnvValue; -use eyre::{eyre, Context}; -use std::{collections::BTreeMap, path::Path, process::Command}; - -#[derive(Debug, clap::Args)] -/// Run build commands provided in the given dataflow. -pub struct Build { - /// Path to the dataflow descriptor file - #[clap(value_name = "PATH")] - dataflow: String, - // Use UV to build nodes. - #[clap(long, action)] - uv: bool, -} - -impl Executable for Build { - fn execute(self) -> eyre::Result<()> { - default_tracing()?; - build(self.dataflow, self.uv) - } -} - -pub fn build(dataflow: String, uv: bool) -> eyre::Result<()> { - let dataflow = resolve_dataflow(dataflow).context("could not resolve dataflow")?; - let descriptor = Descriptor::blocking_read(&dataflow)?; - let dataflow_absolute = if dataflow.is_relative() { - std::env::current_dir().unwrap().join(dataflow) - } else { - dataflow.to_owned() - }; - let working_dir = dataflow_absolute.parent().unwrap(); - - let default_op_id = OperatorId::from(SINGLE_OPERATOR_DEFAULT_ID.to_string()); - - for node in descriptor.nodes { - match node.kind()? { - dora_core::descriptor::NodeKind::Standard(_) => { - run_build_command(node.build.as_deref(), working_dir, uv, node.env.clone()) - .with_context(|| { - format!("build command failed for standard node `{}`", node.id) - })? - } - dora_core::descriptor::NodeKind::Runtime(runtime_node) => { - for operator in &runtime_node.operators { - run_build_command( - operator.config.build.as_deref(), - working_dir, - uv, - node.env.clone(), - ) - .with_context(|| { - format!( - "build command failed for operator `{}/{}`", - node.id, operator.id - ) - })?; - } - } - dora_core::descriptor::NodeKind::Custom(custom_node) => run_build_command( - custom_node.build.as_deref(), - working_dir, - uv, - node.env.clone(), - ) - .with_context(|| format!("build command failed for custom node `{}`", node.id))?, - dora_core::descriptor::NodeKind::Operator(operator) => run_build_command( - operator.config.build.as_deref(), - working_dir, - uv, - node.env.clone(), - ) - .with_context(|| { - format!( - "build command failed for operator `{}/{}`", - node.id, - operator.id.as_ref().unwrap_or(&default_op_id) - ) - })?, - } - } - - Ok(()) -} - -fn run_build_command( - build: Option<&str>, - working_dir: &Path, - uv: bool, - envs: Option>, -) -> eyre::Result<()> { - if let Some(build) = build { - let lines = build.lines().collect::>(); - for build_line in lines { - let mut split = build_line.split_whitespace(); - - let program = split - .next() - .ok_or_else(|| eyre!("build command is empty"))?; - let mut cmd = if uv && (program == "pip" || program == "pip3") { - let mut cmd = Command::new("uv"); - cmd.arg("pip"); - cmd - } else { - Command::new(program) - }; - cmd.args(split); - - // Inject Environment Variables - if let Some(envs) = envs.clone() { - for (key, value) in envs { - let value = value.to_string(); - cmd.env(key, value); - } - } - - cmd.current_dir(working_dir); - let exit_status = cmd - .status() - .wrap_err_with(|| format!("failed to run `{}`", build))?; - if !exit_status.success() { - return Err(eyre!("build command `{build_line}` returned {exit_status}")); - } - } - Ok(()) - } else { - Ok(()) - } -} diff --git a/binaries/cli/src/command/build/distributed.rs b/binaries/cli/src/command/build/distributed.rs new file mode 100644 index 00000000..1f6591fe --- /dev/null +++ b/binaries/cli/src/command/build/distributed.rs @@ -0,0 +1,107 @@ +use communication_layer_request_reply::{TcpConnection, TcpRequestReplyConnection}; +use dora_core::descriptor::Descriptor; +use dora_message::{ + cli_to_coordinator::ControlRequest, + common::{GitSource, LogMessage}, + coordinator_to_cli::ControlRequestReply, + id::NodeId, + BuildId, +}; +use eyre::{bail, Context}; +use std::{ + collections::BTreeMap, + net::{SocketAddr, TcpStream}, +}; + +use crate::{output::print_log_message, session::DataflowSession}; + +pub fn build_distributed_dataflow( + session: &mut TcpRequestReplyConnection, + dataflow: Descriptor, + git_sources: &BTreeMap, + dataflow_session: &DataflowSession, + local_working_dir: Option, + uv: bool, +) -> eyre::Result { + let build_id = { + let reply_raw = session + .request( + &serde_json::to_vec(&ControlRequest::Build { + session_id: dataflow_session.session_id, + dataflow, + git_sources: git_sources.clone(), + prev_git_sources: dataflow_session.git_sources.clone(), + local_working_dir, + uv, + }) + .unwrap(), + ) + .wrap_err("failed to send start dataflow message")?; + + let result: ControlRequestReply = + serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + match result { + ControlRequestReply::DataflowBuildTriggered { build_id } => { + eprintln!("dataflow build triggered: {build_id}"); + build_id + } + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected start dataflow reply: {other:?}"), + } + }; + Ok(build_id) +} + +pub fn wait_until_dataflow_built( + build_id: BuildId, + session: &mut TcpRequestReplyConnection, + coordinator_socket: SocketAddr, + log_level: log::LevelFilter, +) -> eyre::Result { + // subscribe to log messages + let mut log_session = TcpConnection { + stream: TcpStream::connect(coordinator_socket) + .wrap_err("failed to connect to dora coordinator")?, + }; + log_session + .send( + &serde_json::to_vec(&ControlRequest::BuildLogSubscribe { + build_id, + level: log_level, + }) + .wrap_err("failed to serialize message")?, + ) + .wrap_err("failed to send build log subscribe request to coordinator")?; + std::thread::spawn(move || { + while let Ok(raw) = log_session.receive() { + let parsed: eyre::Result = + serde_json::from_slice(&raw).context("failed to parse log message"); + match parsed { + Ok(log_message) => { + print_log_message(log_message, false, true); + } + Err(err) => { + tracing::warn!("failed to parse log message: {err:?}") + } + } + } + }); + + let reply_raw = session + .request(&serde_json::to_vec(&ControlRequest::WaitForBuild { build_id }).unwrap()) + .wrap_err("failed to send WaitForBuild message")?; + + let result: ControlRequestReply = + serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + match result { + ControlRequestReply::DataflowBuildFinished { build_id, result } => match result { + Ok(()) => { + eprintln!("dataflow build finished successfully"); + Ok(build_id) + } + Err(err) => bail!("{err}"), + }, + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected start dataflow reply: {other:?}"), + } +} \ No newline at end of file diff --git a/binaries/cli/src/command/build/git.rs b/binaries/cli/src/command/build/git.rs new file mode 100644 index 00000000..18faba87 --- /dev/null +++ b/binaries/cli/src/command/build/git.rs @@ -0,0 +1,45 @@ +use dora_message::{common::GitSource, descriptor::GitRepoRev}; +use eyre::Context; + +pub fn fetch_commit_hash(repo_url: String, rev: Option) -> eyre::Result { + let mut remote = git2::Remote::create_detached(repo_url.as_bytes()) + .with_context(|| format!("failed to create git remote for {repo_url}"))?; + let connection = remote + .connect_auth(git2::Direction::Fetch, None, None) + .with_context(|| format!("failed to open connection to {repo_url}"))?; + let references = connection + .list() + .with_context(|| format!("failed to list git references of {repo_url}"))?; + + let expected_name = match &rev { + Some(GitRepoRev::Branch(branch)) => format!("refs/heads/{branch}"), + Some(GitRepoRev::Tag(tag)) => format!("refs/tags/{tag}"), + Some(GitRepoRev::Rev(rev)) => rev.clone(), + None => "HEAD".into(), + }; + + let mut commit_hash = None; + for head in references { + if head.name() == expected_name { + commit_hash = Some(head.oid().to_string()); + break; + } + } + + if commit_hash.is_none() { + if let Some(GitRepoRev::Rev(rev)) = &rev { + // rev might be a commit hash instead of a reference + if rev.is_ascii() && rev.bytes().all(|b| b.is_ascii_alphanumeric()) { + commit_hash = Some(rev.clone()); + } + } + } + + match commit_hash { + Some(commit_hash) => Ok(GitSource { + repo: repo_url, + commit_hash, + }), + None => eyre::bail!("no matching commit for `{rev:?}`"), + } +} diff --git a/binaries/cli/src/command/build/local.rs b/binaries/cli/src/command/build/local.rs new file mode 100644 index 00000000..6fed9747 --- /dev/null +++ b/binaries/cli/src/command/build/local.rs @@ -0,0 +1,121 @@ +use std::{collections::BTreeMap, path::PathBuf}; + +use colored::Colorize; +use dora_core::{ + build::{BuildInfo, BuildLogger, Builder, GitManager, LogLevelOrStdout, PrevGitSource}, + descriptor::{Descriptor, DescriptorExt}, +}; +use dora_message::{common::GitSource, id::NodeId}; +use eyre::Context; + +use crate::session::DataflowSession; + +pub fn build_dataflow_locally( + dataflow: Descriptor, + git_sources: &BTreeMap, + dataflow_session: &DataflowSession, + working_dir: PathBuf, + uv: bool, +) -> eyre::Result { + let runtime = tokio::runtime::Runtime::new()?; + + runtime.block_on(build_dataflow( + dataflow, + git_sources, + dataflow_session, + working_dir, + uv, + )) +} + +async fn build_dataflow( + dataflow: Descriptor, + git_sources: &BTreeMap, + dataflow_session: &DataflowSession, + base_working_dir: PathBuf, + uv: bool, +) -> eyre::Result { + let builder = Builder { + session_id: dataflow_session.session_id, + base_working_dir, + uv, + }; + let nodes = dataflow.resolve_aliases_and_set_defaults()?; + + let mut git_manager = GitManager::default(); + let prev_git_sources = &dataflow_session.git_sources; + + let mut tasks = Vec::new(); + + // build nodes + for node in nodes.into_values() { + let node_id = node.id.clone(); + let git_source = git_sources.get(&node_id).cloned(); + let prev_git_source = prev_git_sources.get(&node_id).cloned(); + let prev_git = prev_git_source.map(|prev_source| PrevGitSource { + still_needed_for_this_build: git_sources.values().any(|s| s == &prev_source), + git_source: prev_source, + }); + + let task = builder + .clone() + .build_node( + node, + git_source, + prev_git, + LocalBuildLogger { + node_id: node_id.clone(), + }, + &mut git_manager, + ) + .await + .wrap_err_with(|| format!("failed to build node `{node_id}`"))?; + tasks.push((node_id, task)); + } + + let mut info = BuildInfo { + node_working_dirs: Default::default(), + }; + for (node_id, task) in tasks { + let node = task + .await + .with_context(|| format!("failed to build node `{node_id}`"))?; + info.node_working_dirs + .insert(node_id, node.node_working_dir); + } + Ok(info) +} + +struct LocalBuildLogger { + node_id: NodeId, +} + +impl BuildLogger for LocalBuildLogger { + type Clone = Self; + + async fn log_message( + &mut self, + level: impl Into + Send, + message: impl Into + Send, + ) { + let level = match level.into() { + LogLevelOrStdout::LogLevel(level) => match level { + log::Level::Error => "ERROR ".red(), + log::Level::Warn => "WARN ".yellow(), + log::Level::Info => "INFO ".green(), + log::Level::Debug => "DEBUG ".bright_blue(), + log::Level::Trace => "TRACE ".dimmed(), + }, + LogLevelOrStdout::Stdout => "stdout".italic().dimmed(), + }; + let node = self.node_id.to_string().bold().bright_black(); + let message: String = message.into(); + println!("{node}: {level} {message}"); + } + + async fn try_clone(&self) -> eyre::Result { + Ok(LocalBuildLogger { + node_id: self.node_id.clone(), + }) + } +} \ No newline at end of file diff --git a/binaries/cli/src/command/build/mod.rs b/binaries/cli/src/command/build/mod.rs new file mode 100644 index 00000000..32eed2e0 --- /dev/null +++ b/binaries/cli/src/command/build/mod.rs @@ -0,0 +1,205 @@ +use communication_layer_request_reply::TcpRequestReplyConnection; +use dora_core::{ + descriptor::{CoreNodeKind, CustomNode, Descriptor, DescriptorExt}, + topics::{DORA_COORDINATOR_PORT_CONTROL_DEFAULT, LOCALHOST}, +}; +use dora_message::{descriptor::NodeSource, BuildId}; +use eyre::Context; +use std::{collections::BTreeMap, net::IpAddr}; + +use super::{default_tracing, Executable}; +use crate::{ + common::{connect_to_coordinator, local_working_dir, resolve_dataflow}, + session::DataflowSession, +}; + +use distributed::{build_distributed_dataflow, wait_until_dataflow_built}; +use local::build_dataflow_locally; + +mod distributed; +mod git; +mod local; + +#[derive(Debug, clap::Args)] +/// Run build commands provided in the given dataflow. +pub struct Build { + /// Path to the dataflow descriptor file + #[clap(value_name = "PATH")] + dataflow: String, + /// Address of the dora coordinator + #[clap(long, value_name = "IP")] + coordinator_addr: Option, + /// Port number of the coordinator control server + #[clap(long, value_name = "PORT")] + coordinator_port: Option, + // Use UV to build nodes. + #[clap(long, action)] + uv: bool, + // Run build on local machine + #[clap(long, action)] + local: bool, +} + +impl Executable for Build { + fn execute(self) -> eyre::Result<()> { + default_tracing()?; + build( + self.dataflow, + self.coordinator_addr, + self.coordinator_port, + self.uv, + self.local, + ) + } +} + +pub fn build( + dataflow: String, + coordinator_addr: Option, + coordinator_port: Option, + uv: bool, + force_local: bool, +) -> eyre::Result<()> { + let dataflow_path = resolve_dataflow(dataflow).context("could not resolve dataflow")?; + let dataflow_descriptor = + Descriptor::blocking_read(&dataflow_path).wrap_err("Failed to read yaml dataflow")?; + let mut dataflow_session = + DataflowSession::read_session(&dataflow_path).context("failed to read DataflowSession")?; + + let mut git_sources = BTreeMap::new(); + let resolved_nodes = dataflow_descriptor + .resolve_aliases_and_set_defaults() + .context("failed to resolve nodes")?; + for (node_id, node) in resolved_nodes { + if let CoreNodeKind::Custom(CustomNode { + source: NodeSource::GitBranch { repo, rev }, + .. + }) = node.kind + { + let source = git::fetch_commit_hash(repo, rev) + .with_context(|| format!("failed to find commit hash for `{node_id}`"))?; + git_sources.insert(node_id, source); + } + } + + let session = || connect_to_coordinator_with_defaults(coordinator_addr, coordinator_port); + + let build_kind = if force_local { + log::info!("Building locally, as requested through `--force-local`"); + BuildKind::Local + } else if dataflow_descriptor.nodes.iter().all(|n| n.deploy.is_none()) { + log::info!("Building locally because dataflow does not contain any `deploy` sections"); + BuildKind::Local + } else if coordinator_addr.is_some() || coordinator_port.is_some() { + log::info!("Building through coordinator, using the given coordinator socket information"); + // explicit coordinator address or port set -> there should be a coordinator running + BuildKind::ThroughCoordinator { + coordinator_session: session().context("failed to connect to coordinator")?, + } + } else { + match session() { + Ok(coordinator_session) => { + // we found a local coordinator instance at default port -> use it for building + log::info!("Found local dora coordinator instance -> building through coordinator"); + BuildKind::ThroughCoordinator { + coordinator_session, + } + } + Err(_) => { + log::warn!("No dora coordinator instance found -> trying a local build"); + // no coordinator instance found -> do a local build + BuildKind::Local + } + } + }; + + match build_kind { + BuildKind::Local => { + log::info!("running local build"); + // use dataflow dir as base working dir + let local_working_dir = dunce::canonicalize(&dataflow_path) + .context("failed to canonicalize dataflow path")? + .parent() + .ok_or_else(|| eyre::eyre!("dataflow path has no parent dir"))? + .to_owned(); + let build_info = build_dataflow_locally( + dataflow_descriptor, + &git_sources, + &dataflow_session, + local_working_dir, + uv, + )?; + + dataflow_session.git_sources = git_sources; + // generate a random BuildId and store the associated build info + dataflow_session.build_id = Some(BuildId::generate()); + dataflow_session.local_build = Some(build_info); + dataflow_session + .write_out_for_dataflow(&dataflow_path) + .context("failed to write out dataflow session file")?; + } + BuildKind::ThroughCoordinator { + mut coordinator_session, + } => { + let local_working_dir = local_working_dir( + &dataflow_path, + &dataflow_descriptor, + &mut *coordinator_session, + )?; + let build_id = build_distributed_dataflow( + &mut *coordinator_session, + dataflow_descriptor, + &git_sources, + &dataflow_session, + local_working_dir, + uv, + )?; + + dataflow_session.git_sources = git_sources; + dataflow_session + .write_out_for_dataflow(&dataflow_path) + .context("failed to write out dataflow session file")?; + + // wait until dataflow build is finished + + wait_until_dataflow_built( + build_id, + &mut *coordinator_session, + coordinator_socket(coordinator_addr, coordinator_port), + log::LevelFilter::Info, + )?; + + dataflow_session.build_id = Some(build_id); + dataflow_session.local_build = None; + dataflow_session + .write_out_for_dataflow(&dataflow_path) + .context("failed to write out dataflow session file")?; + } + }; + + Ok(()) +} + +enum BuildKind { + Local, + ThroughCoordinator { + coordinator_session: Box, + }, +} + +fn connect_to_coordinator_with_defaults( + coordinator_addr: Option, + coordinator_port: Option, +) -> std::io::Result> { + let coordinator_socket = coordinator_socket(coordinator_addr, coordinator_port); + connect_to_coordinator(coordinator_socket) +} + +fn coordinator_socket( + coordinator_addr: Option, + coordinator_port: Option, +) -> std::net::SocketAddr { + let coordinator_addr = coordinator_addr.unwrap_or(LOCALHOST); + let coordinator_port = coordinator_port.unwrap_or(DORA_COORDINATOR_PORT_CONTROL_DEFAULT); + (coordinator_addr, coordinator_port).into() +} From 582d69cc4728541cd5b277cf1771bc637dbe101e Mon Sep 17 00:00:00 2001 From: sjfhsjfh Date: Wed, 25 Jun 2025 23:10:30 +0800 Subject: [PATCH 11/40] chore: remove unused import --- binaries/cli/src/command/mod.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/binaries/cli/src/command/mod.rs b/binaries/cli/src/command/mod.rs index 2004b2a4..7ab3804a 100644 --- a/binaries/cli/src/command/mod.rs +++ b/binaries/cli/src/command/mod.rs @@ -14,16 +14,12 @@ mod start; mod stop; mod up; -use std::path::{Path, PathBuf}; - use build::Build; use check::Check; -use communication_layer_request_reply::TcpRequestReplyConnection; use coordinator::Coordinator; use daemon::Daemon; use destroy::Destroy; -use dora_core::descriptor::Descriptor; -use eyre::{Context, ContextCompat}; +use eyre::Context; use graph::Graph; use list::ListArgs; use logs::LogsArgs; @@ -35,8 +31,6 @@ use start::Start; use stop::Stop; use up::Up; -use crate::common::cli_and_daemon_on_same_machine; - /// dora-rs cli client #[derive(Debug, clap::Subcommand)] pub enum Command { From 31f0b338034d63dcf9599ccfabb9818e9e2dc47b Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 30 Jun 2025 11:53:26 +0200 Subject: [PATCH 12/40] Fix coordinator not using build feature flag --- binaries/coordinator/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binaries/coordinator/Cargo.toml b/binaries/coordinator/Cargo.toml index c11454f9..67ea2d6c 100644 --- a/binaries/coordinator/Cargo.toml +++ b/binaries/coordinator/Cargo.toml @@ -19,7 +19,7 @@ futures = "0.3.21" tokio = { version = "1.24.2", features = ["full"] } tokio-stream = { version = "0.1.8", features = ["io-util", "net"] } uuid = { version = "1.2.1" } -dora-core = { workspace = true } +dora-core = { workspace = true, features = ["build"] } tracing = "0.1.36" dora-tracing = { workspace = true, optional = true } futures-concurrency = "7.1.0" From 7ba0c145b0c623ffb3af1615bd52586ca6277b87 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 30 Jun 2025 14:10:06 +0200 Subject: [PATCH 13/40] Bump dora to v0.3.12 --- Cargo.lock | 88 +++++++++---------- Cargo.toml | 44 +++++----- node-hub/dora-argotranslate/pyproject.toml | 2 +- node-hub/dora-cotracker/pyproject.toml | 2 +- node-hub/dora-distil-whisper/pyproject.toml | 2 +- node-hub/dora-echo/pyproject.toml | 2 +- node-hub/dora-gradio/pyproject.toml | 2 +- node-hub/dora-internvl/pyproject.toml | 2 +- node-hub/dora-ios-lidar/pyproject.toml | 2 +- node-hub/dora-keyboard/pyproject.toml | 2 +- node-hub/dora-kokoro-tts/pyproject.toml | 2 +- node-hub/dora-microphone/pyproject.toml | 2 +- node-hub/dora-object-to-pose/Cargo.toml | 2 +- node-hub/dora-openai-server/pyproject.toml | 2 +- node-hub/dora-opus/pyproject.toml | 2 +- node-hub/dora-outtetts/pyproject.toml | 2 +- node-hub/dora-parler/pyproject.toml | 2 +- node-hub/dora-phi4/pyproject.toml | 2 +- node-hub/dora-piper/pyproject.toml | 2 +- node-hub/dora-pyaudio/pyproject.toml | 2 +- node-hub/dora-pyorbbecksdk/pyproject.toml | 2 +- node-hub/dora-pyrealsense/pyproject.toml | 2 +- node-hub/dora-qwen/pyproject.toml | 2 +- node-hub/dora-qwen2-5-vl/pyproject.toml | 2 +- node-hub/dora-qwenvl/pyproject.toml | 2 +- node-hub/dora-rav1e/Cargo.toml | 2 +- node-hub/dora-rdt-1b/pyproject.toml | 2 +- node-hub/dora-reachy2/pyproject.toml | 2 +- node-hub/dora-sam2/pyproject.toml | 2 +- node-hub/dora-ugv/pyproject.toml | 2 +- node-hub/dora-vad/pyproject.toml | 2 +- node-hub/dora-yolo/pyproject.toml | 2 +- node-hub/feetech-client/pyproject.toml | 2 +- node-hub/gamepad/pyproject.toml | 2 +- .../llama-factory-recorder/pyproject.toml | 2 +- node-hub/opencv-plot/pyproject.toml | 2 +- node-hub/opencv-video-capture/pyproject.toml | 2 +- node-hub/pyarrow-assert/pyproject.toml | 2 +- node-hub/pyarrow-sender/pyproject.toml | 2 +- node-hub/terminal-input/pyproject.toml | 2 +- 40 files changed, 104 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c56d68ff..f5af0ab6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1303,7 +1303,7 @@ dependencies = [ [[package]] name = "benchmark-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -1316,7 +1316,7 @@ dependencies = [ [[package]] name = "benchmark-example-sink" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -2089,7 +2089,7 @@ dependencies = [ [[package]] name = "communication-layer-pub-sub" -version = "0.3.11" +version = "0.3.12" dependencies = [ "flume 0.10.14", "zenoh 0.7.0-rc", @@ -2097,7 +2097,7 @@ dependencies = [ [[package]] name = "communication-layer-request-reply" -version = "0.3.11" +version = "0.3.12" [[package]] name = "concat-idents" @@ -2911,7 +2911,7 @@ dependencies = [ [[package]] name = "dora-arrow-convert" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "chrono", @@ -2922,7 +2922,7 @@ dependencies = [ [[package]] name = "dora-cli" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bat", "clap 4.5.32", @@ -2966,7 +2966,7 @@ dependencies = [ [[package]] name = "dora-coordinator" -version = "0.3.11" +version = "0.3.12" dependencies = [ "ctrlc", "dora-core", @@ -2987,7 +2987,7 @@ dependencies = [ [[package]] name = "dora-core" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-message", "dunce", @@ -3011,7 +3011,7 @@ dependencies = [ [[package]] name = "dora-daemon" -version = "0.3.11" +version = "0.3.12" dependencies = [ "aligned-vec", "async-trait", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "dora-dav1d" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bitstream-io", "bytemuck", @@ -3062,7 +3062,7 @@ dependencies = [ [[package]] name = "dora-download" -version = "0.3.11" +version = "0.3.12" dependencies = [ "eyre", "reqwest", @@ -3092,7 +3092,7 @@ dependencies = [ [[package]] name = "dora-kit-car" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "dotenv", @@ -3127,7 +3127,7 @@ dependencies = [ [[package]] name = "dora-metrics" -version = "0.3.11" +version = "0.3.12" dependencies = [ "eyre", "opentelemetry 0.29.1", @@ -3148,7 +3148,7 @@ dependencies = [ [[package]] name = "dora-node-api" -version = "0.3.11" +version = "0.3.12" dependencies = [ "aligned-vec", "arrow", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "dora-node-api-c" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow-array", "dora-node-api", @@ -3183,7 +3183,7 @@ dependencies = [ [[package]] name = "dora-node-api-cxx" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "cxx", @@ -3201,7 +3201,7 @@ dependencies = [ [[package]] name = "dora-node-api-python" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "dora-cli", @@ -3222,7 +3222,7 @@ dependencies = [ [[package]] name = "dora-object-to-pose" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -3231,7 +3231,7 @@ dependencies = [ [[package]] name = "dora-openai-proxy-server" -version = "0.3.11" +version = "0.3.12" dependencies = [ "chrono", "dora-node-api", @@ -3252,7 +3252,7 @@ dependencies = [ [[package]] name = "dora-operator-api" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-arrow-convert", "dora-operator-api-macros", @@ -3261,14 +3261,14 @@ dependencies = [ [[package]] name = "dora-operator-api-c" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-operator-api-types", ] [[package]] name = "dora-operator-api-cxx" -version = "0.3.11" +version = "0.3.12" dependencies = [ "cxx", "cxx-build", @@ -3277,7 +3277,7 @@ dependencies = [ [[package]] name = "dora-operator-api-macros" -version = "0.3.11" +version = "0.3.12" dependencies = [ "proc-macro2", "quote", @@ -3286,7 +3286,7 @@ dependencies = [ [[package]] name = "dora-operator-api-python" -version = "0.3.11" +version = "0.3.12" dependencies = [ "aligned-vec", "arrow", @@ -3302,7 +3302,7 @@ dependencies = [ [[package]] name = "dora-operator-api-types" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "dora-arrow-convert", @@ -3311,7 +3311,7 @@ dependencies = [ [[package]] name = "dora-rav1e" -version = "0.3.11+fix1" +version = "0.3.12" dependencies = [ "avif-serialize", "bytemuck", @@ -3324,7 +3324,7 @@ dependencies = [ [[package]] name = "dora-record" -version = "0.3.11" +version = "0.3.12" dependencies = [ "chrono", "dora-node-api", @@ -3336,7 +3336,7 @@ dependencies = [ [[package]] name = "dora-rerun" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bytemuck", "dora-node-api", @@ -3351,7 +3351,7 @@ dependencies = [ [[package]] name = "dora-ros2-bridge" -version = "0.3.11" +version = "0.3.12" dependencies = [ "array-init", "dora-daemon", @@ -3374,7 +3374,7 @@ dependencies = [ [[package]] name = "dora-ros2-bridge-msg-gen" -version = "0.3.11" +version = "0.3.12" dependencies = [ "anyhow", "glob", @@ -3390,7 +3390,7 @@ dependencies = [ [[package]] name = "dora-ros2-bridge-python" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "dora-ros2-bridge", @@ -3404,7 +3404,7 @@ dependencies = [ [[package]] name = "dora-runtime" -version = "0.3.11" +version = "0.3.12" dependencies = [ "aligned-vec", "arrow", @@ -3443,7 +3443,7 @@ dependencies = [ [[package]] name = "dora-tracing" -version = "0.3.11" +version = "0.3.12" dependencies = [ "eyre", "opentelemetry 0.18.0", @@ -6982,7 +6982,7 @@ dependencies = [ [[package]] name = "multiple-daemons-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -6993,14 +6993,14 @@ dependencies = [ [[package]] name = "multiple-daemons-example-operator" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-operator-api", ] [[package]] name = "multiple-daemons-example-sink" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11017,7 +11017,7 @@ checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" [[package]] name = "receive_data" -version = "0.3.11" +version = "0.3.12" dependencies = [ "chrono", "dora-node-api", @@ -11457,7 +11457,7 @@ dependencies = [ [[package]] name = "rust-dataflow-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11468,7 +11468,7 @@ dependencies = [ [[package]] name = "rust-dataflow-example-sink" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11476,7 +11476,7 @@ dependencies = [ [[package]] name = "rust-dataflow-example-sink-dynamic" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11484,7 +11484,7 @@ dependencies = [ [[package]] name = "rust-dataflow-example-status-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11503,7 +11503,7 @@ dependencies = [ [[package]] name = "rust-ros2-dataflow-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "dora-ros2-bridge", @@ -12347,7 +12347,7 @@ dependencies = [ [[package]] name = "shared-memory-server" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bincode", "eyre", @@ -13173,7 +13173,7 @@ dependencies = [ [[package]] name = "terminal-print" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", diff --git a/Cargo.toml b/Cargo.toml index 5080b5a7..6bbde97a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,34 +50,34 @@ members = [ [workspace.package] edition = "2021" # Make sure to also bump `apis/node/python/__init__.py` version. -version = "0.3.11" +version = "0.3.12" description = "`dora` goal is to be a low latency, composable, and distributed data flow." documentation = "https://dora-rs.ai" license = "Apache-2.0" repository = "https://github.com/dora-rs/dora/" [workspace.dependencies] -dora-node-api = { version = "0.3.11", path = "apis/rust/node", default-features = false } -dora-node-api-python = { version = "0.3.11", path = "apis/python/node", default-features = false } -dora-operator-api = { version = "0.3.11", path = "apis/rust/operator", default-features = false } -dora-operator-api-macros = { version = "0.3.11", path = "apis/rust/operator/macros" } -dora-operator-api-types = { version = "0.3.11", path = "apis/rust/operator/types" } -dora-operator-api-python = { version = "0.3.11", path = "apis/python/operator" } -dora-operator-api-c = { version = "0.3.11", path = "apis/c/operator" } -dora-node-api-c = { version = "0.3.11", path = "apis/c/node" } -dora-core = { version = "0.3.11", path = "libraries/core" } -dora-arrow-convert = { version = "0.3.11", path = "libraries/arrow-convert" } -dora-tracing = { version = "0.3.11", path = "libraries/extensions/telemetry/tracing" } -dora-metrics = { version = "0.3.11", path = "libraries/extensions/telemetry/metrics" } -dora-download = { version = "0.3.11", path = "libraries/extensions/download" } -shared-memory-server = { version = "0.3.11", path = "libraries/shared-memory-server" } -communication-layer-request-reply = { version = "0.3.11", path = "libraries/communication-layer/request-reply" } -dora-cli = { version = "0.3.11", path = "binaries/cli" } -dora-runtime = { version = "0.3.11", path = "binaries/runtime" } -dora-daemon = { version = "0.3.11", path = "binaries/daemon" } -dora-coordinator = { version = "0.3.11", path = "binaries/coordinator" } -dora-ros2-bridge = { version = "0.3.11", path = "libraries/extensions/ros2-bridge" } -dora-ros2-bridge-msg-gen = { version = "0.3.11", path = "libraries/extensions/ros2-bridge/msg-gen" } +dora-node-api = { version = "0.3.12", path = "apis/rust/node", default-features = false } +dora-node-api-python = { version = "0.3.12", path = "apis/python/node", default-features = false } +dora-operator-api = { version = "0.3.12", path = "apis/rust/operator", default-features = false } +dora-operator-api-macros = { version = "0.3.12", path = "apis/rust/operator/macros" } +dora-operator-api-types = { version = "0.3.12", path = "apis/rust/operator/types" } +dora-operator-api-python = { version = "0.3.12", path = "apis/python/operator" } +dora-operator-api-c = { version = "0.3.12", path = "apis/c/operator" } +dora-node-api-c = { version = "0.3.12", path = "apis/c/node" } +dora-core = { version = "0.3.12", path = "libraries/core" } +dora-arrow-convert = { version = "0.3.12", path = "libraries/arrow-convert" } +dora-tracing = { version = "0.3.12", path = "libraries/extensions/telemetry/tracing" } +dora-metrics = { version = "0.3.12", path = "libraries/extensions/telemetry/metrics" } +dora-download = { version = "0.3.12", path = "libraries/extensions/download" } +shared-memory-server = { version = "0.3.12", path = "libraries/shared-memory-server" } +communication-layer-request-reply = { version = "0.3.12", path = "libraries/communication-layer/request-reply" } +dora-cli = { version = "0.3.12", path = "binaries/cli" } +dora-runtime = { version = "0.3.12", path = "binaries/runtime" } +dora-daemon = { version = "0.3.12", path = "binaries/daemon" } +dora-coordinator = { version = "0.3.12", path = "binaries/coordinator" } +dora-ros2-bridge = { version = "0.3.12", path = "libraries/extensions/ros2-bridge" } +dora-ros2-bridge-msg-gen = { version = "0.3.12", path = "libraries/extensions/ros2-bridge/msg-gen" } dora-ros2-bridge-python = { path = "libraries/extensions/ros2-bridge/python" } # versioned independently from the other dora crates dora-message = { version = "0.5.0-alpha", path = "libraries/message" } diff --git a/node-hub/dora-argotranslate/pyproject.toml b/node-hub/dora-argotranslate/pyproject.toml index a440d567..3a0feb57 100644 --- a/node-hub/dora-argotranslate/pyproject.toml +++ b/node-hub/dora-argotranslate/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-argotranslate" -version = "0.3.11" +version = "0.3.12" description = "Dora Node for Text translating using Argostranslate" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, diff --git a/node-hub/dora-cotracker/pyproject.toml b/node-hub/dora-cotracker/pyproject.toml index a27d21c6..713a78c8 100644 --- a/node-hub/dora-cotracker/pyproject.toml +++ b/node-hub/dora-cotracker/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-cotracker" -version = "0.1.0" +version = "0.3.12" authors = [{ name = "Shashwat Patil", email = "shashwatpatil974@gmail.com" }] description = "A Dora node implementing real-time object tracking using Facebook's CoTracker model" license = "CC-BY-1.0" diff --git a/node-hub/dora-distil-whisper/pyproject.toml b/node-hub/dora-distil-whisper/pyproject.toml index 56ea1532..68451ca4 100644 --- a/node-hub/dora-distil-whisper/pyproject.toml +++ b/node-hub/dora-distil-whisper/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-distil-whisper" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-echo/pyproject.toml b/node-hub/dora-echo/pyproject.toml index 8db2e285..2390a86c 100644 --- a/node-hub/dora-echo/pyproject.toml +++ b/node-hub/dora-echo/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-echo" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-gradio/pyproject.toml b/node-hub/dora-gradio/pyproject.toml index 77d907ac..a0427ab2 100644 --- a/node-hub/dora-gradio/pyproject.toml +++ b/node-hub/dora-gradio/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-gradio" -version = "0.2.0" +version = "0.3.12" authors = [{ name = "Shashwat Patil", email = "email@email.com" }] description = "dora-gradio" license = { text = "MIT" } diff --git a/node-hub/dora-internvl/pyproject.toml b/node-hub/dora-internvl/pyproject.toml index d5cf2eba..aa6b8f02 100644 --- a/node-hub/dora-internvl/pyproject.toml +++ b/node-hub/dora-internvl/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-internvl" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-ios-lidar/pyproject.toml b/node-hub/dora-ios-lidar/pyproject.toml index f09480b8..a9123826 100644 --- a/node-hub/dora-ios-lidar/pyproject.toml +++ b/node-hub/dora-ios-lidar/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-ios-lidar" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Your Name", email = "email@email.com" }] description = "dora-ios-lidar" license = { text = "MIT" } diff --git a/node-hub/dora-keyboard/pyproject.toml b/node-hub/dora-keyboard/pyproject.toml index 62f02828..aed1e39b 100644 --- a/node-hub/dora-keyboard/pyproject.toml +++ b/node-hub/dora-keyboard/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-keyboard" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-kokoro-tts/pyproject.toml b/node-hub/dora-kokoro-tts/pyproject.toml index 712b3188..14abf6e7 100644 --- a/node-hub/dora-kokoro-tts/pyproject.toml +++ b/node-hub/dora-kokoro-tts/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-kokoro-tts" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Your Name", email = "email@email.com" }] description = "dora-kokoro-tts" license = { text = "MIT" } diff --git a/node-hub/dora-microphone/pyproject.toml b/node-hub/dora-microphone/pyproject.toml index 4e3ccae8..fc535556 100644 --- a/node-hub/dora-microphone/pyproject.toml +++ b/node-hub/dora-microphone/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-microphone" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-object-to-pose/Cargo.toml b/node-hub/dora-object-to-pose/Cargo.toml index aa527514..e858893a 100644 --- a/node-hub/dora-object-to-pose/Cargo.toml +++ b/node-hub/dora-object-to-pose/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dora-object-to-pose" -version = "0.3.11" +version = "0.3.12" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/node-hub/dora-openai-server/pyproject.toml b/node-hub/dora-openai-server/pyproject.toml index 8b29cec9..69679ab4 100644 --- a/node-hub/dora-openai-server/pyproject.toml +++ b/node-hub/dora-openai-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-openai-server" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-opus/pyproject.toml b/node-hub/dora-opus/pyproject.toml index 39596485..1db30aba 100644 --- a/node-hub/dora-opus/pyproject.toml +++ b/node-hub/dora-opus/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-opus" -version = "0.3.11" +version = "0.3.12" description = "Dora Node for Text translating using Opus" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, diff --git a/node-hub/dora-outtetts/pyproject.toml b/node-hub/dora-outtetts/pyproject.toml index 32cd9369..561ec4ef 100644 --- a/node-hub/dora-outtetts/pyproject.toml +++ b/node-hub/dora-outtetts/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-outtetts" -version = "0.3.11" +version = "0.3.12" authors = [] description = "dora-outtetts" license = { text = "MIT" } diff --git a/node-hub/dora-parler/pyproject.toml b/node-hub/dora-parler/pyproject.toml index 10c16ac0..690336b9 100644 --- a/node-hub/dora-parler/pyproject.toml +++ b/node-hub/dora-parler/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-parler" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-phi4/pyproject.toml b/node-hub/dora-phi4/pyproject.toml index 984f781d..c6ba0d11 100644 --- a/node-hub/dora-phi4/pyproject.toml +++ b/node-hub/dora-phi4/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-phi4" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Somay", email = "ssomay2002@gmail.com" }] description = "DORA node for Phi-4 multimodal model" license = { text = "MIT" } diff --git a/node-hub/dora-piper/pyproject.toml b/node-hub/dora-piper/pyproject.toml index c526a12e..acf18712 100644 --- a/node-hub/dora-piper/pyproject.toml +++ b/node-hub/dora-piper/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-piper" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] description = "Dora Node for using Agilex piper" license = { text = "MIT" } diff --git a/node-hub/dora-pyaudio/pyproject.toml b/node-hub/dora-pyaudio/pyproject.toml index bf3a62d6..d9cb7191 100644 --- a/node-hub/dora-pyaudio/pyproject.toml +++ b/node-hub/dora-pyaudio/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-pyaudio" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] license = { text = "MIT" } readme = "README.md" diff --git a/node-hub/dora-pyorbbecksdk/pyproject.toml b/node-hub/dora-pyorbbecksdk/pyproject.toml index 08d28afd..a6152c4c 100644 --- a/node-hub/dora-pyorbbecksdk/pyproject.toml +++ b/node-hub/dora-pyorbbecksdk/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-pyorbbecksdk" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Xiang Yang", email = "Ryu-Yang@qq.com" }, diff --git a/node-hub/dora-pyrealsense/pyproject.toml b/node-hub/dora-pyrealsense/pyproject.toml index 9edba374..60f052f9 100644 --- a/node-hub/dora-pyrealsense/pyproject.toml +++ b/node-hub/dora-pyrealsense/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-pyrealsense" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] description = "Dora Node for capturing video with Pyrealsense" license = { text = "MIT" } diff --git a/node-hub/dora-qwen/pyproject.toml b/node-hub/dora-qwen/pyproject.toml index 83ac58ea..d3832ddb 100644 --- a/node-hub/dora-qwen/pyproject.toml +++ b/node-hub/dora-qwen/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-qwen" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Your Name", email = "email@email.com" }] description = "dora-qwen" license = { text = "MIT" } diff --git a/node-hub/dora-qwen2-5-vl/pyproject.toml b/node-hub/dora-qwen2-5-vl/pyproject.toml index 39aa23e9..d415501a 100644 --- a/node-hub/dora-qwen2-5-vl/pyproject.toml +++ b/node-hub/dora-qwen2-5-vl/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-qwen2-5-vl" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-qwenvl/pyproject.toml b/node-hub/dora-qwenvl/pyproject.toml index 7ae47892..d8e4c1e7 100644 --- a/node-hub/dora-qwenvl/pyproject.toml +++ b/node-hub/dora-qwenvl/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-qwenvl" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/dora-rav1e/Cargo.toml b/node-hub/dora-rav1e/Cargo.toml index c2e35cd2..30a39070 100644 --- a/node-hub/dora-rav1e/Cargo.toml +++ b/node-hub/dora-rav1e/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "dora-rav1e" edition = "2021" -version = "0.3.11+fix1" +version = "0.3.12" description.workspace = true documentation.workspace = true license = "BSD-2-Clause" diff --git a/node-hub/dora-rdt-1b/pyproject.toml b/node-hub/dora-rdt-1b/pyproject.toml index e8bcfe12..580386f7 100644 --- a/node-hub/dora-rdt-1b/pyproject.toml +++ b/node-hub/dora-rdt-1b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-rdt-1b" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] description = "Dora Node for RDT 1B" license = { text = "MIT" } diff --git a/node-hub/dora-reachy2/pyproject.toml b/node-hub/dora-reachy2/pyproject.toml index 3b64479f..ec5bb3bf 100644 --- a/node-hub/dora-reachy2/pyproject.toml +++ b/node-hub/dora-reachy2/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-reachy2" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Your Name", email = "email@email.com" }] description = "dora-reachy2" license = { text = "MIT" } diff --git a/node-hub/dora-sam2/pyproject.toml b/node-hub/dora-sam2/pyproject.toml index 6ad124b8..d3453ac5 100644 --- a/node-hub/dora-sam2/pyproject.toml +++ b/node-hub/dora-sam2/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-sam2" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Your Name", email = "email@email.com" }] description = "dora-sam2" license = { text = "MIT" } diff --git a/node-hub/dora-ugv/pyproject.toml b/node-hub/dora-ugv/pyproject.toml index 3ae0421f..c27e1d14 100644 --- a/node-hub/dora-ugv/pyproject.toml +++ b/node-hub/dora-ugv/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-ugv" -version = "0.3.11" +version = "0.3.12" authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] description = "Dora Node for using Agilex UGV" license = { text = "MIT" } diff --git a/node-hub/dora-vad/pyproject.toml b/node-hub/dora-vad/pyproject.toml index d9e91472..cbeca935 100644 --- a/node-hub/dora-vad/pyproject.toml +++ b/node-hub/dora-vad/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-vad" -version = "0.3.11" +version = "0.3.12" description = "Dora Node for Text translating using Argostranslate" authors = [{ name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }] license = { text = "MIT" } diff --git a/node-hub/dora-yolo/pyproject.toml b/node-hub/dora-yolo/pyproject.toml index 4e7047b8..8d5f19f1 100644 --- a/node-hub/dora-yolo/pyproject.toml +++ b/node-hub/dora-yolo/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-yolo" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/feetech-client/pyproject.toml b/node-hub/feetech-client/pyproject.toml index c5f3426a..2dc1ac04 100644 --- a/node-hub/feetech-client/pyproject.toml +++ b/node-hub/feetech-client/pyproject.toml @@ -8,7 +8,7 @@ readme = "README.md" requires-python = ">=3.9" dependencies = [ - "dora-rs == 0.3.11", + "dora-rs == 0.3.12", "numpy <= 2.0.0", "feetech-servo-sdk == 1.0.0", "pwm-position-control", diff --git a/node-hub/gamepad/pyproject.toml b/node-hub/gamepad/pyproject.toml index 902526af..5dee5dc5 100644 --- a/node-hub/gamepad/pyproject.toml +++ b/node-hub/gamepad/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gamepad" -version = "0.1.0" +version = "0.3.12" authors = [{ name = "Shashwat Patil", email = "email@email.com" }] description = "gamepad" license = { text = "MIT" } diff --git a/node-hub/llama-factory-recorder/pyproject.toml b/node-hub/llama-factory-recorder/pyproject.toml index 4a894bb2..66976bd8 100644 --- a/node-hub/llama-factory-recorder/pyproject.toml +++ b/node-hub/llama-factory-recorder/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "llama-factory-recorder" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/opencv-plot/pyproject.toml b/node-hub/opencv-plot/pyproject.toml index 6b4412a4..64e428f5 100644 --- a/node-hub/opencv-plot/pyproject.toml +++ b/node-hub/opencv-plot/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "opencv-plot" -version = "0.3.11" +version = "0.3.12" license = { file = "MIT" } authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, diff --git a/node-hub/opencv-video-capture/pyproject.toml b/node-hub/opencv-video-capture/pyproject.toml index 9baee6ea..3b55ec64 100644 --- a/node-hub/opencv-video-capture/pyproject.toml +++ b/node-hub/opencv-video-capture/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "opencv-video-capture" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/pyarrow-assert/pyproject.toml b/node-hub/pyarrow-assert/pyproject.toml index 8a3ef699..4efc4d90 100644 --- a/node-hub/pyarrow-assert/pyproject.toml +++ b/node-hub/pyarrow-assert/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pyarrow-assert" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/pyarrow-sender/pyproject.toml b/node-hub/pyarrow-sender/pyproject.toml index e3d8f9bc..c1410594 100644 --- a/node-hub/pyarrow-sender/pyproject.toml +++ b/node-hub/pyarrow-sender/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pyarrow-sender" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, diff --git a/node-hub/terminal-input/pyproject.toml b/node-hub/terminal-input/pyproject.toml index 78d9503b..691008fd 100644 --- a/node-hub/terminal-input/pyproject.toml +++ b/node-hub/terminal-input/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "terminal-input" -version = "0.3.11" +version = "0.3.12" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, From a4e7ac6b85c46166a6ae4258cee0952830f33a1e Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 30 Jun 2025 14:13:15 +0200 Subject: [PATCH 14/40] Adding updates --- Changelog.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 55 insertions(+) diff --git a/Changelog.md b/Changelog.md index 88fe1117..fd475f72 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,59 @@ # Changelog +## v0.3.12 (2025-06-30) + +## What's Changed + +- Implemented dora-cotracker node by @ShashwatPatil in https://github.com/dora-rs/dora/pull/931 +- Minor fix and add boxes2d example to facebook/cotracker by @haixuanTao in https://github.com/dora-rs/dora/pull/950 +- Update Rust crate tokio to v1.44.2 [SECURITY] by @renovate in https://github.com/dora-rs/dora/pull/951 +- Post 3.11 release fix by @haixuanTao in https://github.com/dora-rs/dora/pull/954 +- Bump crossbeam-channel from 0.5.14 to 0.5.15 by @dependabot in https://github.com/dora-rs/dora/pull/959 +- Added E ruff flag for pydocstyle by @7SOMAY in https://github.com/dora-rs/dora/pull/958 +- Revert "Added E ruff flag for better code quality [skip ci]" by @haixuanTao in https://github.com/dora-rs/dora/pull/968 +- Ease of use changes in benches for issue #957 by @Ignavar in https://github.com/dora-rs/dora/pull/969 +- Reachy cotracker by @haixuanTao in https://github.com/dora-rs/dora/pull/972 +- Improve rav1e by @haixuanTao in https://github.com/dora-rs/dora/pull/974 +- Fix pyrealsense by @haixuanTao in https://github.com/dora-rs/dora/pull/973 +- Added Self Uninstall Command by @Shar-jeel-Sajid in https://github.com/dora-rs/dora/pull/944 +- Improve benchmark implementation & Add warning for discarding events by @Mivik in https://github.com/dora-rs/dora/pull/971 +- docs: Updated README: Added comprehensive usage documentation with vi… by @LeonRust in https://github.com/dora-rs/dora/pull/983 +- Fix rerun-viewer example. by @francocipollone in https://github.com/dora-rs/dora/pull/989 +- docs: add license badge by @Radovenchyk in https://github.com/dora-rs/dora/pull/996 +- Disable sccache for `musllinux` jobs by @haixuanTao in https://github.com/dora-rs/dora/pull/1000 +- Remove unused sysinfo monitor by @Mivik in https://github.com/dora-rs/dora/pull/1007 +- Refactor Python CUDA IPC API by @Mivik in https://github.com/dora-rs/dora/pull/1002 +- fix terminal not printing stdout on nvml warning by @haixuanTao in https://github.com/dora-rs/dora/pull/1008 +- Fix issue #1006: [Brief description of the fix] by @sohamukute in https://github.com/dora-rs/dora/pull/1013 +- Improving so100 usability by @haixuanTao in https://github.com/dora-rs/dora/pull/988 +- Add dora-mediapipe node for quick human pose estimation by @haixuanTao in https://github.com/dora-rs/dora/pull/986 +- Bump torch to 2.7 by @haixuanTao in https://github.com/dora-rs/dora/pull/1015 +- refactor(tracing): use builder style by @sjfhsjfh in https://github.com/dora-rs/dora/pull/1009 +- Fix spawning runtime through python when it is installed with pip by @haixuanTao in https://github.com/dora-rs/dora/pull/1011 +- chore(deps): update dependency numpy to v2 by @renovate in https://github.com/dora-rs/dora/pull/1014 +- Fix error when multiple visualization key is active and when urdf_transform env variable is not present by @haixuanTao in https://github.com/dora-rs/dora/pull/1016 +- Update pyrealsense2 Dependencies for L515 Support and Fix README wget Link by @kingchou007 in https://github.com/dora-rs/dora/pull/1021 +- Minor fix for mujoco sim by @haixuanTao in https://github.com/dora-rs/dora/pull/1023 +- dora-mujoco simulation node with example for controlling any arm by @ShashwatPatil in https://github.com/dora-rs/dora/pull/1012 +- fix ros CI/CD by @haixuanTao in https://github.com/dora-rs/dora/pull/1027 +- dora-vggt by @haixuanTao in https://github.com/dora-rs/dora/pull/1024 +- Adding vision to openai server by @haixuanTao in https://github.com/dora-rs/dora/pull/1025 +- Revert "Adding vision to openai server" by @haixuanTao in https://github.com/dora-rs/dora/pull/1031 +- Expose AllInputClosed message as a Stop message by @haixuanTao in https://github.com/dora-rs/dora/pull/1026 +- Add support for git repository sources for nodes by @phil-opp in https://github.com/dora-rs/dora/pull/901 +- Adding vision to rust openai proxy server by @haixuanTao in https://github.com/dora-rs/dora/pull/1033 +- Add automatic robot descriptions URDF retrieval from https://github.com/robot-descriptions/robot_descriptions.py by @haixuanTao in https://github.com/dora-rs/dora/pull/1032 + +## New Contributors + +- @Mivik made their first contribution in https://github.com/dora-rs/dora/pull/971 +- @francocipollone made their first contribution in https://github.com/dora-rs/dora/pull/989 +- @sohamukute made their first contribution in https://github.com/dora-rs/dora/pull/1013 +- @sjfhsjfh made their first contribution in https://github.com/dora-rs/dora/pull/1009 +- @kingchou007 made their first contribution in https://github.com/dora-rs/dora/pull/1021 + +**Full Changelog**: https://github.com/dora-rs/dora/compare/v0.3.11...v0.3.12 + ## v0.3.11 (2025-04-07) ## What's Changed diff --git a/README.md b/README.md index 967198ff..faaf3c30 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@
2025 +- \[06/25\] Add support for git based node, dora-vggt for multi-camera depth estimation, and adding robot_descriptions_py as a default way to get urdfs within dora. - \[05/25\] Add support for dora-pytorch-kinematics for fk and ik, dora-mediapipe for pose estimation, dora-rustypot for rust serialport read/write, points2d and points3d visualization in rerun. - \[04/25\] Add support for dora-cotracker to track any point on a frame, dora-rav1e AV1 encoding up to 12bit and dora-dav1d AV1 decoding, - \[03/25\] Add support for dora async Python. From 39c933436ad67d1f5b2521798026e94c30cfa52e Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 30 Jun 2025 14:15:24 +0200 Subject: [PATCH 15/40] Bump dora message into non alpha version --- Cargo.lock | 2 +- Cargo.toml | 2 +- libraries/message/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5af0ab6..e03b96b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3106,7 +3106,7 @@ dependencies = [ [[package]] name = "dora-message" -version = "0.5.0-alpha" +version = "0.5.0" dependencies = [ "aligned-vec", "arrow-data", diff --git a/Cargo.toml b/Cargo.toml index 6bbde97a..9c433b1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ dora-ros2-bridge = { version = "0.3.12", path = "libraries/extensions/ros2-bridg dora-ros2-bridge-msg-gen = { version = "0.3.12", path = "libraries/extensions/ros2-bridge/msg-gen" } dora-ros2-bridge-python = { path = "libraries/extensions/ros2-bridge/python" } # versioned independently from the other dora crates -dora-message = { version = "0.5.0-alpha", path = "libraries/message" } +dora-message = { version = "0.5.0", path = "libraries/message" } arrow = { version = "54.2.1" } arrow-schema = { version = "54.2.1" } arrow-data = { version = "54.2.1" } diff --git a/libraries/message/Cargo.toml b/libraries/message/Cargo.toml index 3bcc71fe..41f6ee20 100644 --- a/libraries/message/Cargo.toml +++ b/libraries/message/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "dora-message" # versioned separately from the other dora crates -version = "0.5.0-alpha" +version = "0.5.0" edition.workspace = true documentation.workspace = true description.workspace = true From d6e55e1eae1ad577ea434963728914bf3570eaed Mon Sep 17 00:00:00 2001 From: haixuantao Date: Tue, 1 Jul 2025 13:51:27 +0200 Subject: [PATCH 16/40] Add vggt based environment simulation --- examples/urdf/vggt/franka.yml | 70 ++++++++++++++++++++++++++++ examples/urdf/vggt/kuka.yml | 68 +++++++++++++++++++++++++++ examples/urdf/vggt/so_arm101.yml | 69 +++++++++++++++++++++++++++ examples/urdf/vggt/z1.yml | 59 +++++++++++++++++++++++ node-hub/dora-vggt/dora_vggt/main.py | 17 +++---- 5 files changed, 273 insertions(+), 10 deletions(-) create mode 100644 examples/urdf/vggt/franka.yml create mode 100644 examples/urdf/vggt/kuka.yml create mode 100644 examples/urdf/vggt/so_arm101.yml create mode 100644 examples/urdf/vggt/z1.yml diff --git a/examples/urdf/vggt/franka.yml b/examples/urdf/vggt/franka.yml new file mode 100644 index 00000000..40a715ed --- /dev/null +++ b/examples/urdf/vggt/franka.yml @@ -0,0 +1,70 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_panda: pytorch_kinematics/cmd_vel + camera/image: dora-vggt/image + camera/depth: dora-vggt/depth + env: + panda_urdf: "panda_description" + panda_transform: .5 -0. -0.1 1. 0. 0. 0. + CAMERA_PITCH: 1.5708 + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "panda_description" + END_EFFECTOR_LINK: "panda_link8" + TRANSFORM: .5 -0. -0.1 1. 0. 0. 0. + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 + + - id: camera + build: pip install -e ../../../node-hub/opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 4 + + - id: camera2 + build: pip install -e ../../../node-hub/opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 6 + + - id: dora-vggt + build: pip install -e ../../../node-hub/dora-vggt + path: dora-vggt + inputs: + image: camera/image + image2: camera2/image + outputs: + - depth + - image + env: + SCALE_FACTOR: 0.9 diff --git a/examples/urdf/vggt/kuka.yml b/examples/urdf/vggt/kuka.yml new file mode 100644 index 00000000..ad4fd383 --- /dev/null +++ b/examples/urdf/vggt/kuka.yml @@ -0,0 +1,68 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_iiwa14_primitive_collision: pytorch_kinematics/cmd_vel + camera/image: dora-vggt/image + camera/depth: dora-vggt/depth + env: + iiwa14_primitive_collision_urdf: "iiwa14_description" + iiwa14_primitive_collision_transform: .5 -0. -0.1 1. 0. 0. 0. + CAMERA_PITCH: 1.5708 + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.02 + MAX_ANGULAR_SPEED: 0.10 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "iiwa14_description" + END_EFFECTOR_LINK: "iiwa_link_7" + TRANSFORM: .5 -0. -0.1 1. 0. 0. 0. + + - id: camera + build: pip install -e ../../../node-hub/opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 4 + + - id: camera2 + build: pip install -e ../../../node-hub/opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 6 + + - id: dora-vggt + build: pip install -e ../../../node-hub/dora-vggt + path: dora-vggt + inputs: + image: camera/image + image2: camera2/image + outputs: + - depth + - image + env: + SCALE_FACTOR: 0.9 diff --git a/examples/urdf/vggt/so_arm101.yml b/examples/urdf/vggt/so_arm101.yml new file mode 100644 index 00000000..ea9e878a --- /dev/null +++ b/examples/urdf/vggt/so_arm101.yml @@ -0,0 +1,69 @@ +nodes: + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_so101_new_calib: pytorch_kinematics/cmd_vel + camera/image: dora-vggt/image + camera/depth: dora-vggt/depth + env: + so101_new_calib_urdf: "so_arm101_description" + so101_new_calib_transform: .14 -0. 0.4 -.5 .5 .5 -.5 + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "so_arm101_description" + END_EFFECTOR_LINK: "gripper" + TRANSFORM: .14 -0. 0.4 -.5 .5 .5 -.5 + POSITION_TOLERANCE: 0.01 + ROTATION_TOLERANCE: 0.03 + + - id: camera + build: pip install -e ../../../node-hub/opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 4 + + - id: camera2 + build: pip install -e ../../../node-hub/opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 6 + + - id: dora-vggt + build: pip install -e ../../../node-hub/dora-vggt + path: dora-vggt + inputs: + image: camera/image + image2: camera2/image + outputs: + - depth + - image + env: + SCALE_FACTOR: 0.9 diff --git a/examples/urdf/vggt/z1.yml b/examples/urdf/vggt/z1.yml new file mode 100644 index 00000000..801e1de2 --- /dev/null +++ b/examples/urdf/vggt/z1.yml @@ -0,0 +1,59 @@ +nodes: + - id: plot + build: pip install -e ../../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_z1: pytorch_kinematics/cmd_vel + camera/image: dora-vggt/image + camera/depth: dora-vggt/depth + env: + z1_urdf: z1_description + z1_transform: .5 -0.2 -0.11 1. 0. 0. 0. + CAMERA_PITCH: 1.5708 + + - id: gamepad + build: pip install -e ../../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 + + - id: pytorch_kinematics + build: pip install -e ../../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "z1_description" + END_EFFECTOR_LINK: "link06" + TRANSFORM: .5 -0.2 -0.11 1. 0. 0. 0. + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 + + - id: camera + build: pip install -e ../../../node-hub/opencv-video-capture + path: opencv-video-capture + inputs: + tick: dora/timer/millis/100 + outputs: + - image + env: + CAPTURE_PATH: 4 + + - id: dora-vggt + build: pip install -e ../../../node-hub/dora-vggt + path: dora-vggt + inputs: + image: camera/image + outputs: + - depth + - image + env: + SCALE_FACTOR: 0.88 diff --git a/node-hub/dora-vggt/dora_vggt/main.py b/node-hub/dora-vggt/dora_vggt/main.py index 7c0e24c7..9cab97b8 100644 --- a/node-hub/dora-vggt/dora_vggt/main.py +++ b/node-hub/dora-vggt/dora_vggt/main.py @@ -1,5 +1,5 @@ """TODO: Add docstring.""" - +import os import io from collections import deque as Deque @@ -13,6 +13,8 @@ from vggt.models.vggt import VGGT from vggt.utils.load_fn import load_and_preprocess_images from vggt.utils.pose_enc import pose_encoding_to_extri_intri +SCALE_FACTOR = float(os.getenv("SCALE_FACTOR", "1")) +VGGT_NUM_IMAGES = int(os.getenv("VGGT_NUM_IMAGES", "2")) # bfloat16 is supported on Ampere GPUs (Compute Capability 8.0+) dtype = torch.bfloat16 @@ -28,7 +30,7 @@ model.eval() def main(): """TODO: Add docstring.""" node = Node() - raw_images = Deque(maxlen=2) + raw_images = Deque(maxlen=VGGT_NUM_IMAGES) for event in node: if event["type"] == "INPUT": @@ -100,15 +102,14 @@ def main(): depth_map, depth_conf = model.depth_head( aggregated_tokens_list, images, ps_idx ) - print(depth_conf.max()) depth_map[depth_conf < 1.0] = 0.0 # Set low confidence pixels to 0 depth_map = depth_map.to(torch.float64) depth_map = depth_map[-1][-1].cpu().numpy() - + depth_map = SCALE_FACTOR * depth_map # Warning: Make sure to add my_output_id and my_input_id within the dataflow. node.send_output( - output_id="depth", + output_id=event["id"].replace("image", "depth"), data=pa.array(depth_map.ravel()), metadata={ "width": depth_map.shape[1], @@ -129,13 +130,9 @@ def main(): # reorder pixels to be in last dimension image = image.transpose(1, 2, 0) - print( - f"Image shape: {image.shape}, dtype: {image.dtype} and depth map shape: {depth_map.shape}, dtype: {depth_map.dtype}" - ) - # Warning: Make sure to add my_output_id and my_input_id within the dataflow. node.send_output( - output_id="image", + output_id=event["id"], data=pa.array(image.ravel()), metadata={ "encoding": "rgb8", From 7b8a2656de4d9b0f6809410f22a8d6c462539480 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Tue, 1 Jul 2025 14:47:31 +0200 Subject: [PATCH 17/40] Minor fix --- node-hub/dora-vggt/dora_vggt/main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/node-hub/dora-vggt/dora_vggt/main.py b/node-hub/dora-vggt/dora_vggt/main.py index 9cab97b8..e35d174c 100644 --- a/node-hub/dora-vggt/dora_vggt/main.py +++ b/node-hub/dora-vggt/dora_vggt/main.py @@ -1,7 +1,7 @@ """TODO: Add docstring.""" -import os import io -from collections import deque as Deque +import os +from collections import deque import cv2 import numpy as np @@ -30,7 +30,7 @@ model.eval() def main(): """TODO: Add docstring.""" node = Node() - raw_images = Deque(maxlen=VGGT_NUM_IMAGES) + raw_images = deque(maxlen=VGGT_NUM_IMAGES) for event in node: if event["type"] == "INPUT": @@ -90,7 +90,7 @@ def main(): pose_enc = model.camera_head(aggregated_tokens_list)[-1] # Extrinsic and intrinsic matrices, following OpenCV convention (camera from world) extrinsic, intrinsic = pose_encoding_to_extri_intri( - pose_enc, images.shape[-2:] + pose_enc, images.shape[-2:], ) intrinsic = intrinsic[-1][-1] f_0 = intrinsic[0, 0] @@ -100,7 +100,7 @@ def main(): # Predict Depth Maps depth_map, depth_conf = model.depth_head( - aggregated_tokens_list, images, ps_idx + aggregated_tokens_list, images, ps_idx, ) depth_map[depth_conf < 1.0] = 0.0 # Set low confidence pixels to 0 depth_map = depth_map.to(torch.float64) From 29cb67d59ca7ebd46d3916a5ea857b8ed876300c Mon Sep 17 00:00:00 2001 From: ShashwatPatil Date: Thu, 26 Jun 2025 23:34:23 +0530 Subject: [PATCH 18/40] add teleop and gamepad control for so101 --- examples/so101/Readme.md | 60 ++++++++++++++++++++++++++ examples/so101/arm_gamepad_control.yml | 48 +++++++++++++++++++++ examples/so101/leader_follower.yml | 33 ++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 examples/so101/Readme.md create mode 100644 examples/so101/arm_gamepad_control.yml create mode 100644 examples/so101/leader_follower.yml diff --git a/examples/so101/Readme.md b/examples/so101/Readme.md new file mode 100644 index 00000000..efd18880 --- /dev/null +++ b/examples/so101/Readme.md @@ -0,0 +1,60 @@ +## SO101 Arm Control + +This example provides gamepad control and leader-follower functionality for the SO-101 robotic arm. + +### Install Dependencies + +install the required Python packages for rerun visualization (optional): + +```bash +# Install the URDF loader for Rerun visualization +pip install git+https://github.com/dora-rs/rerun-loader-python-urdf +``` + +### Hardware Setup + +1. Connect your SO-101 arm(s) to your computer via USB/serial +2. Note the serial port names (e.g.,for linux `/dev/ttyACM0`, `/dev/ttyACM1`) +3. Connect your gamepad controller +4. Update the `PORT` environment variable in the YAML files + +#### Single Arm Control (arm_gamepad_control.yml) + +Control a single SO-101 arm with gamepad input and visualization: + +```bash +dora build arm.yml +dora run arm.yml +``` + +#### Leader-Follower Mode (leader_follower.yml) + +Use one arm as a leader to control another follower arm: + +```bash +dora build leader.yml +dora run leader.yml +``` + +#### Serial Port Configuration + +Update the `PORT` environment variable in the YAML files: + +```yaml +env: + PORT: /dev/ttyACM0 # Change to your actual port +``` + +## Troubleshooting + +### Serial Connection Issues +- Check that the arm is powered on and connected +- Verify the correct serial port in the YAML configuration +- Ensure proper permissions: `sudo chmod +x PORT` + +### Gamepad Not Detected +- Verify gamepad is connected and recognized by the system +- Test with `jstest /dev/input/js0` (Linux) + +## Safety Notes +- Always ensure the arm has sufficient clearance before operation \ No newline at end of file diff --git a/examples/so101/arm_gamepad_control.yml b/examples/so101/arm_gamepad_control.yml new file mode 100644 index 00000000..85190100 --- /dev/null +++ b/examples/so101/arm_gamepad_control.yml @@ -0,0 +1,48 @@ +nodes: + - id: so101 + build: pip install -e ../../node-hub/dora-rustypot + path: dora-rustypot + inputs: + tick: dora/timer/millis/10 + pose: pytorch_kinematics/cmd_vel + outputs: + - pose + env: + PORT: /dev/ttyACM0 + IDS: 1 2 3 4 5 + + - id: pytorch_kinematics + build: pip install -e ../../node-hub/dora-pytorch-kinematics + path: dora-pytorch-kinematics + inputs: + cmd_vel: gamepad/cmd_vel + outputs: + - cmd_vel + env: + MODEL_NAME: "so_arm101_description" + END_EFFECTOR_LINK: "gripper" + TRANSFORM: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.01 + ROTATION_TOLERANCE: 0.03 + + - id: gamepad + build: pip install -e ../../node-hub/gamepad + path: gamepad + outputs: + - cmd_vel + - raw_control + inputs: + tick: dora/timer/millis/10 + env: + MAX_LINEAR_SPEED: 0.01 + MAX_ANGULAR_SPEED: 0.05 + + # comment below path if you don't want to visualize the arm in rerun + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_so101_new_calib: pytorch_kinematics/cmd_vel + env: + so101_new_calib_urdf: "so_arm101_description" + so101_new_calib_transform: "0. 0. 0. 1. 0. 0. 0." diff --git a/examples/so101/leader_follower.yml b/examples/so101/leader_follower.yml new file mode 100644 index 00000000..b73ef4a3 --- /dev/null +++ b/examples/so101/leader_follower.yml @@ -0,0 +1,33 @@ +nodes: + - id: so101 + build: pip install -e ../../node-hub/dora-rustypot + path: dora-rustypot + inputs: + tick: dora/timer/millis/10 + pose: leader_interface/pose + outputs: + - pose + env: + PORT: /dev/ttyACM0 + IDS: 1 2 3 4 5 6 + + - id: leader_interface + build: pip install -e ../../node-hub/dora-rustypot + path: dora-rustypot + inputs: + tick: dora/timer/millis/10 + outputs: + - pose + env: + PORT: /dev/ttyACM1 + IDS: 1 2 3 4 5 6 + + # comment below path if you don't want to visualize the arms in rerun + - id: plot + build: pip install -e ../../node-hub/dora-rerun + path: dora-rerun + inputs: + jointstate_so101_new_calib: so101/pose + env: + so101_new_calib_urdf: "so_arm101_description" + so101_new_calib_transform: "0. 0. 0. 1. 0. 0. 0." \ No newline at end of file From df6633bd49d5e15ba1d8aa0c0e34b4f8bfcb4274 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Tue, 1 Jul 2025 15:05:10 +0200 Subject: [PATCH 19/40] Use actual pose in visualization instead of pytorch kinematics --- examples/so101/arm_gamepad_control.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/so101/arm_gamepad_control.yml b/examples/so101/arm_gamepad_control.yml index 85190100..9ddacd55 100644 --- a/examples/so101/arm_gamepad_control.yml +++ b/examples/so101/arm_gamepad_control.yml @@ -20,7 +20,7 @@ nodes: - cmd_vel env: MODEL_NAME: "so_arm101_description" - END_EFFECTOR_LINK: "gripper" + END_EFFECTOR_LINK: "gripper" TRANSFORM: "0. 0. 0. 1. 0. 0. 0." POSITION_TOLERANCE: 0.01 ROTATION_TOLERANCE: 0.03 @@ -37,12 +37,12 @@ nodes: MAX_LINEAR_SPEED: 0.01 MAX_ANGULAR_SPEED: 0.05 - # comment below path if you don't want to visualize the arm in rerun + # comment below path if you don't want to visualize the arm in rerun - id: plot build: pip install -e ../../node-hub/dora-rerun path: dora-rerun inputs: - jointstate_so101_new_calib: pytorch_kinematics/cmd_vel + jointstate_so101_new_calib: so101/pose env: so101_new_calib_urdf: "so_arm101_description" so101_new_calib_transform: "0. 0. 0. 1. 0. 0. 0." From da52b8c30c6a56b88ddc6bcc58e9473be7442474 Mon Sep 17 00:00:00 2001 From: Edgar Riba Date: Thu, 3 Jul 2025 13:33:43 +0200 Subject: [PATCH 20/40] add kornia nodes and example --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index faaf3c30..a4dde388 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@
2025 +- \[07/25\] Added Kornia rust nodes in the hub for V4L / Gstreamer cameras and Sobel image processing. - \[06/25\] Add support for git based node, dora-vggt for multi-camera depth estimation, and adding robot_descriptions_py as a default way to get urdfs within dora. - \[05/25\] Add support for dora-pytorch-kinematics for fk and ik, dora-mediapipe for pose estimation, dora-rustypot for rust serialport read/write, points2d and points3d visualization in rerun. - \[04/25\] Add support for dora-cotracker to track any point on a frame, dora-rav1e AV1 encoding up to 12bit and dora-dav1d AV1 decoding, @@ -103,6 +104,8 @@ | Camera | [PyOrbbeckSDK](https://github.com/dora-rs/dora/blob/main/node-hub/dora-pyorbbecksdk) | πŸ“ | Image and depth from Orbbeck Camera | ![Downloads](https://img.shields.io/pypi/dm/dora-pyorbbecksdk?label=%20) | ![License](https://img.shields.io/pypi/l/dora-pyorbbecksdk?label=%20) | | Camera | [PyRealsense](https://github.com/dora-rs/dora/blob/main/node-hub/dora-pyrealsense) | LinuxπŸ†—
MacπŸ› οΈ | Image and depth from Realsense | ![Downloads](https://img.shields.io/pypi/dm/dora-pyrealsense?label=%20) | ![License](https://img.shields.io/pypi/l/dora-pyrealsense?label=%20) | | Camera | [OpenCV Video Capture](https://github.com/dora-rs/dora/blob/main/node-hub/opencv-video-capture) | βœ… | Image stream from OpenCV Camera | ![Downloads](https://img.shields.io/pypi/dm/opencv-video-capture?label=%20) | ![License](https://img.shields.io/pypi/l/opencv-video-capture?label=%20) | +| Camera | [Kornia V4L Capture](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-v4l-capture) | βœ… | Video stream for Linux Camera (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | +| Camera | [Kornia GST Capture](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-gst-capture) | βœ… | Video Capture using Gstreamer (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | | Peripheral | [Keyboard](https://github.com/dora-rs/dora/blob/main/node-hub/dora-keyboard) | βœ… | Keyboard char listener | ![Downloads](https://img.shields.io/pypi/dm/dora-keyboard?label=%20) | ![License](https://img.shields.io/pypi/l/dora-keyboard?label=%20) | | Peripheral | [Microphone](https://github.com/dora-rs/dora/blob/main/node-hub/dora-microphone) | βœ… | Audio from microphone | ![Downloads](https://img.shields.io/pypi/dm/dora-microphone?label=%20) | ![License](https://img.shields.io/pypi/l/dora-microphone?label=%20) | | Peripheral | [PyAudio(Speaker)](https://github.com/dora-rs/dora/blob/main/node-hub/dora-pyaudio) | βœ… | Output audio from speaker | ![Downloads](https://img.shields.io/pypi/dm/dora-pyaudio?label=%20) | ![License](https://img.shields.io/pypi/l/dora-pyaudio?label=%20) | @@ -135,6 +138,7 @@ | Simulator | [Mujoco](https://github.com/dora-rs/dora-lerobot/blob/main/node-hub/mujoco-client) | πŸ“ | Mujoco Simulator | | | | Simulator | [Carla](https://github.com/dora-rs/dora-drives) | πŸ“ | Carla Simulator | | | | Simulator | [Gymnasium](https://github.com/dora-rs/dora-lerobot/blob/main/gym_dora) | πŸ“ | Experimental OpenAI Gymnasium bridge | | | +| Image Processing | [Kornia Sobel Operator](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-imgproc-sobel) | βœ… | Kornia image processing Sobel operator (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | ## Examples @@ -145,6 +149,7 @@ | Vision | [Vision Language Model(VLM)](https://github.com/dora-rs/dora/blob/main/examples/vlm) | Use a VLM to understand images. | ![License](https://img.shields.io/github/last-commit/dora-rs/dora?path=examples%2Fvlm&label=%20) | | Vision | [YOLO](https://github.com/dora-rs/dora/blob/main/examples/python-dataflow) | Use YOLO to detect object within image. | ![License](https://img.shields.io/github/last-commit/dora-rs/dora?path=examples%2Fpython-dataflow&label=%20) | | Vision | [Camera](https://github.com/dora-rs/dora/blob/main/examples/camera) | Simple webcam plot example | ![License](https://img.shields.io/github/last-commit/dora-rs/dora?path=examples%2Fcamera&label=%20) | +| Vision | [Image Processing](https://github.com/kornia/kornia-rs/tree/main/examples/dora) | Multi camera image processing | | | Model Training | [Piper RDT](https://github.com/dora-rs/dora/blob/main/examples/piper) | Piper RDT Pipeline | ![License](https://img.shields.io/github/last-commit/dora-rs/dora?path=examples%2Fpiper&label=%20) | | Model Training | [LeRobot - Alexander Koch](https://raw.githubusercontent.com/dora-rs/dora-lerobot/refs/heads/main/README.md) | Training Alexander Koch Low Cost Robot with LeRobot | ![License](https://img.shields.io/github/last-commit/dora-rs/dora-lerobot?path=robots&label=%20) | | ROS2 | [C++ ROS2 Example](https://github.com/dora-rs/dora/blob/main/examples/c++-ros2-dataflow) | Example using C++ ROS2 | ![License](https://img.shields.io/github/last-commit/dora-rs/dora?path=examples%2Fc%2b%2b-ros2-dataflow&label=%20) | From e40b534d89cf5db5431fdceb7b29ff60425f6d8b Mon Sep 17 00:00:00 2001 From: haixuantao Date: Thu, 3 Jul 2025 18:10:43 +0200 Subject: [PATCH 21/40] fix qwenvl text only --- node-hub/dora-qwen2-5-vl/dora_qwen2_5_vl/main.py | 4 ++++ node-hub/dora-qwen2-5-vl/pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/node-hub/dora-qwen2-5-vl/dora_qwen2_5_vl/main.py b/node-hub/dora-qwen2-5-vl/dora_qwen2_5_vl/main.py index 898b444d..7592bdad 100644 --- a/node-hub/dora-qwen2-5-vl/dora_qwen2_5_vl/main.py +++ b/node-hub/dora-qwen2-5-vl/dora_qwen2_5_vl/main.py @@ -73,6 +73,10 @@ def generate( messages = [] + # If the texts is string, convert it to a list + if isinstance(texts, str): + texts = [texts] + for text in texts: if text.startswith("<|system|>\n"): messages.append( diff --git a/node-hub/dora-qwen2-5-vl/pyproject.toml b/node-hub/dora-qwen2-5-vl/pyproject.toml index d415501a..95b9a5a3 100644 --- a/node-hub/dora-qwen2-5-vl/pyproject.toml +++ b/node-hub/dora-qwen2-5-vl/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dora-qwen2-5-vl" -version = "0.3.12" +version = "0.3.12.post1" authors = [ { name = "Haixuan Xavier Tao", email = "tao.xavier@outlook.com" }, { name = "Enzo Le Van", email = "dev@enzo-le-van.fr" }, From 73d7cf14e5c39fa82d3c61ef86094c534c813b9c Mon Sep 17 00:00:00 2001 From: haixuantao Date: Fri, 4 Jul 2025 11:41:58 +0200 Subject: [PATCH 22/40] Fix CI/CD and make it possible to run the node hub script on all platform --- .github/workflows/node_hub_test.sh | 33 +- node-hub/dora-phi4/pyproject.toml | 1 - node-hub/dora-phi4/uv.lock | 1309 ++++++++++++++++++++++++++++ 3 files changed, 1340 insertions(+), 3 deletions(-) create mode 100644 node-hub/dora-phi4/uv.lock diff --git a/.github/workflows/node_hub_test.sh b/.github/workflows/node_hub_test.sh index 39ae2b7f..d7e6e4b5 100755 --- a/.github/workflows/node_hub_test.sh +++ b/.github/workflows/node_hub_test.sh @@ -1,6 +1,9 @@ #!/bin/bash set -euo +# Check if we are running in a GitHub Actions environment +CI=${GITHUB_ACTIONS:-false} + # List of ignored modules ignored_folders=("dora-parler" "dora-opus" "dora-internvl" "dora-magma") @@ -13,6 +16,32 @@ dir=$(pwd) # Get the base name of the directory (without the path) base_dir=$(basename "$dir") +# Large node list requiring space cleanup +large_node=("dora-phi4") + +export PYTEST_ADDOPTS="-x" + +# Check if the current directory is in the large node list and if we're in the CI environment +if [[ " ${large_node[@]} " =~ " ${base_dir} " ]] && [[ "$CI" == "true" ]]; then + echo "Running cleanup for $base_dir..." + sudo rm -rf /opt/hostedtoolcache/CodeQL || : + # 1.4GB + sudo rm -rf /opt/hostedtoolcache/go || : + # 489MB + sudo rm -rf /opt/hostedtoolcache/PyPy || : + # 376MB + sudo rm -rf /opt/hostedtoolcache/node || : + # Remove Web browser packages + sudo apt purge -y \ + firefox \ + google-chrome-stable \ + microsoft-edge-stable + sudo rm -rf /usr/local/lib/android/ + sudo rm -rf /usr/share/dotnet/ + sudo rm -rf /opt/ghc/ +fi + + # Check if the directory name is in the ignored list if [[ " ${ignored_folders[@]} " =~ " ${base_dir} " ]]; then echo "Skipping $base_dir as we cannot test it on the CI..." @@ -69,7 +98,7 @@ else maturin publish --target x86_64-apple-darwin --skip-existing --zig fi - elif [[ "$(uname)" = "Linux" ]]; then + elif [[ "$(uname)" = "Linux" ]] || [[ "$CI" == "false" ]]; then if [ -f "$dir/Cargo.toml" ]; then echo "Running build and tests for Rust project in $dir..." cargo check @@ -94,7 +123,7 @@ else else uv run pytest fi - if [ "$GITHUB_EVENT_NAME" == "release" ] || [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then + if [ "${GITHUB_EVENT_NAME:-false}" == "release" ] || [ "${GITHUB_EVENT_NAME:-false}" == "workflow_dispatch" ]; then uv build uv publish --check-url https://pypi.org/simple fi diff --git a/node-hub/dora-phi4/pyproject.toml b/node-hub/dora-phi4/pyproject.toml index c6ba0d11..f1dd9635 100644 --- a/node-hub/dora-phi4/pyproject.toml +++ b/node-hub/dora-phi4/pyproject.toml @@ -18,7 +18,6 @@ dependencies = [ "scipy==1.15.2", "backoff==2.2.1", "peft==0.13.2", - "bitsandbytes>=0.42.0", "opencv-python", "requests", ] diff --git a/node-hub/dora-phi4/uv.lock b/node-hub/dora-phi4/uv.lock new file mode 100644 index 00000000..9f715b8e --- /dev/null +++ b/node-hub/dora-phi4/uv.lock @@ -0,0 +1,1309 @@ +version = 1 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version < '3.11' and platform_system == 'Darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and platform_system == 'Linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version < '3.11' and platform_system != 'Darwin' and platform_system != 'Linux')", + "python_full_version == '3.11.*' and platform_system == 'Darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')", + "python_full_version >= '3.12' and platform_system == 'Darwin'", + "python_full_version >= '3.12' and platform_machine == 'aarch64' and platform_system == 'Linux'", + "(python_full_version >= '3.12' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version >= '3.12' and platform_system != 'Darwin' and platform_system != 'Linux')", +] + +[[package]] +name = "accelerate" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/15/0fab0260ab4069e5224e637d2e400538bb27b0dfc36f17daf68db9770d78/accelerate-1.3.0.tar.gz", hash = "sha256:518631c0adb80bd3d42fb29e7e2dc2256bcd7c786b0ba9119bbaa08611b36d9c", size = 342758 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/de/64508cb91af013aaba214752309c0967568a4219d50a4ea30e822af3c976/accelerate-1.3.0-py3-none-any.whl", hash = "sha256:5788d9e6a7a9f80fed665cf09681c4dddd9dc056bea656db4140ffc285ce423e", size = 336647 }, +] + +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, +] + +[[package]] +name = "certifi" +version = "2025.6.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650 }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818 }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649 }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045 }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356 }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471 }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317 }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368 }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491 }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695 }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849 }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091 }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445 }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782 }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794 }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846 }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350 }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657 }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260 }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164 }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571 }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952 }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959 }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030 }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015 }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106 }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402 }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936 }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790 }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924 }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626 }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567 }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957 }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408 }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399 }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815 }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537 }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565 }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357 }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776 }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "dora-phi4" +version = "0.3.12" +source = { virtual = "." } +dependencies = [ + { name = "accelerate" }, + { name = "backoff" }, + { name = "dora-rs" }, + { name = "opencv-python" }, + { name = "peft" }, + { name = "pillow" }, + { name = "requests" }, + { name = "scipy" }, + { name = "soundfile" }, + { name = "torch" }, + { name = "torchvision" }, + { name = "transformers" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "accelerate", specifier = "==1.3.0" }, + { name = "backoff", specifier = "==2.2.1" }, + { name = "dora-rs", specifier = ">=0.3.9" }, + { name = "opencv-python" }, + { name = "peft", specifier = "==0.13.2" }, + { name = "pillow", specifier = "==11.1.0" }, + { name = "requests" }, + { name = "scipy", specifier = "==1.15.2" }, + { name = "soundfile", specifier = "==0.13.1" }, + { name = "torch", specifier = "==2.6.0" }, + { name = "torchvision", specifier = "==0.21.0" }, + { name = "transformers", specifier = "==4.48.2" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.1.1" }, + { name = "ruff", specifier = ">=0.9.1" }, +] + +[[package]] +name = "dora-rs" +version = "0.3.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyarrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/3f/db3c0c57f3fec666d90001b8222536b40f2c50538daf324a42ffb0f1e6b0/dora_rs-0.3.12.tar.gz", hash = "sha256:92148d5bb62d3b354f712df292302d3af1a96253776bf9010bff0d972dd3adde", size = 304323 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/71/c7e2ed0742a52edb1e3d9308acfa2d2f045e20a12a4e963b23f78dad0ca4/dora_rs-0.3.12-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:f0d8f4debfa1d622a9d42579eb5e3da6fbef8609c76aea90e6c2a462640c3f24", size = 15731753 }, + { url = "https://files.pythonhosted.org/packages/2a/dc/ae34d047f082e41e0bdc031ba6d0aeae708de53316dac4ddb6c23d256000/dora_rs-0.3.12-cp37-abi3-macosx_14_5_arm64.whl", hash = "sha256:c28c0283c9d604666adfdb5233fdd74fc30c5c2d12113faa6b2d2c00792c9631", size = 15710477 }, + { url = "https://files.pythonhosted.org/packages/f9/9a/266ba644b1b88d8dce7442bf07491a7efcbb370472a0f81e311f68ab6b20/dora_rs-0.3.12-cp37-abi3-macosx_14_5_x86_64.whl", hash = "sha256:e66e7da4a530e331bc7a578455d8159ddcbcc28937b4244531eac213dc5a0994", size = 16002397 }, + { url = "https://files.pythonhosted.org/packages/2a/14/cc23e5223bf85ec5d5406c96b0e1c1790a34cee1fd8e527e1c6d6fa96578/dora_rs-0.3.12-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:829255a71cbd0934a8cd294575e6844c4f2ebe4eddb3d99ee88168cf5db425d9", size = 14929902 }, + { url = "https://files.pythonhosted.org/packages/92/00/a27d38fa670fe18c29e87d46bd7c870864c786c6167ff0453d014786653e/dora_rs-0.3.12-cp37-abi3-manylinux_2_28_armv7l.whl", hash = "sha256:a72855b881371357128a49205f95e1a4699673f589c8650b470de975276a667e", size = 13679078 }, + { url = "https://files.pythonhosted.org/packages/91/33/cdb694980687760799682f5a262fa534b2a7f84de6d09990b484051b9540/dora_rs-0.3.12-cp37-abi3-manylinux_2_28_i686.whl", hash = "sha256:13e03213f3fc7b303263822628debc64d28daad6e4bc63c316123df9ee8a0b93", size = 16513457 }, + { url = "https://files.pythonhosted.org/packages/97/6c/97b22218705dfdd00677bf1cadd390f06db25f85bb769c0d1c43fdf9a54a/dora_rs-0.3.12-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b95aceaaa62d7f238c52f26528aec96ee2e3c0666a2248ebf7d266f042ffbe7a", size = 15380271 }, + { url = "https://files.pythonhosted.org/packages/25/43/7abb80782ea81614e021162e2fe4ebd26e65e7ff3923b67d9b546759ce78/dora_rs-0.3.12-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cf5d400ee03a1370c3eea585ad0d056c765c343e97a3cff89f49ecb354f9e39", size = 18290009 }, + { url = "https://files.pythonhosted.org/packages/7c/a1/1ef9640a095555cf56955c003ecc22f4f903b685786a34f08d2483d4ab6c/dora_rs-0.3.12-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:2c86cf88ae5435f1b2fb6d904c1962c46f6487bcc58b8677f27355798429f7d4", size = 17380980 }, + { url = "https://files.pythonhosted.org/packages/32/b7/237f736088f464bdb565438b053fa9934969b7a2bd5a38ebb82d6b226c2b/dora_rs-0.3.12-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:a606c447fa438dd6aad3dbbfdfb2595c3ef706c4d40dd00ae6a57472a9d295f9", size = 18334458 }, + { url = "https://files.pythonhosted.org/packages/e5/fd/ecbc6c76673f25e155027b3f6f7f127992564de056a3615ed6e52629bf6c/dora_rs-0.3.12-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ef4b18bc234a3c9d56a229dfa8db78f8e1315ed913206857f1bd58ad1d0db1a5", size = 18350716 }, + { url = "https://files.pythonhosted.org/packages/05/02/7f0b2fedeba03971b9f63d5815f066ebdb00c99a0ffc2a4c0252ba3ad4ee/dora_rs-0.3.12-cp37-abi3-win_amd64.whl", hash = "sha256:1335dd11db1eb64d9e36b5fc21ff3c316f455c7fa1ade064e9168e146ea92d97", size = 13242066 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "fsspec" +version = "2025.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/f7/27f15d41f0ed38e8fcc488584b57e902b331da7f7c6dcda53721b15838fc/fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475", size = 303033 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052 }, +] + +[[package]] +name = "hf-xet" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/d4/7685999e85945ed0d7f0762b686ae7015035390de1161dcea9d5276c134c/hf_xet-1.1.5.tar.gz", hash = "sha256:69ebbcfd9ec44fdc2af73441619eeb06b94ee34511bbcf57cd423820090f5694", size = 495969 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/89/a1119eebe2836cb25758e7661d6410d3eae982e2b5e974bcc4d250be9012/hf_xet-1.1.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f52c2fa3635b8c37c7764d8796dfa72706cc4eded19d638331161e82b0792e23", size = 2687929 }, + { url = "https://files.pythonhosted.org/packages/de/5f/2c78e28f309396e71ec8e4e9304a6483dcbc36172b5cea8f291994163425/hf_xet-1.1.5-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9fa6e3ee5d61912c4a113e0708eaaef987047616465ac7aa30f7121a48fc1af8", size = 2556338 }, + { url = "https://files.pythonhosted.org/packages/6d/2f/6cad7b5fe86b7652579346cb7f85156c11761df26435651cbba89376cd2c/hf_xet-1.1.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc874b5c843e642f45fd85cda1ce599e123308ad2901ead23d3510a47ff506d1", size = 3102894 }, + { url = "https://files.pythonhosted.org/packages/d0/54/0fcf2b619720a26fbb6cc941e89f2472a522cd963a776c089b189559447f/hf_xet-1.1.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dbba1660e5d810bd0ea77c511a99e9242d920790d0e63c0e4673ed36c4022d18", size = 3002134 }, + { url = "https://files.pythonhosted.org/packages/f3/92/1d351ac6cef7c4ba8c85744d37ffbfac2d53d0a6c04d2cabeba614640a78/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ab34c4c3104133c495785d5d8bba3b1efc99de52c02e759cf711a91fd39d3a14", size = 3171009 }, + { url = "https://files.pythonhosted.org/packages/c9/65/4b2ddb0e3e983f2508528eb4501288ae2f84963586fbdfae596836d5e57a/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:83088ecea236d5113de478acb2339f92c95b4fb0462acaa30621fac02f5a534a", size = 3279245 }, + { url = "https://files.pythonhosted.org/packages/f0/55/ef77a85ee443ae05a9e9cba1c9f0dd9241eb42da2aeba1dc50f51154c81a/hf_xet-1.1.5-cp37-abi3-win_amd64.whl", hash = "sha256:73e167d9807d166596b4b2f0b585c6d5bd84a26dea32843665a8b58f6edba245", size = 2738931 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/42/8a95c5632080ae312c0498744b2b852195e10b05a20b1be11c5141092f4c/huggingface_hub-0.33.2.tar.gz", hash = "sha256:84221defaec8fa09c090390cd68c78b88e3c4c2b7befba68d3dc5aacbc3c2c5f", size = 426637 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/f4/5f3f22e762ad1965f01122b42dae5bf0e009286e2dba601ce1d0dba72424/huggingface_hub-0.33.2-py3-none-any.whl", hash = "sha256:3749498bfa91e8cde2ddc2c1db92c79981f40e66434c20133b39e5928ac9bcc5", size = 515373 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440 } +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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149 }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866 }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506 }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190 }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828 }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006 }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765 }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736 }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719 }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072 }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213 }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632 }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532 }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885 }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467 }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144 }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217 }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014 }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935 }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122 }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143 }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260 }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225 }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666 }, +] + +[[package]] +name = "nvidia-cublas-cu12" +version = "12.4.5.8" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/7f/7fbae15a3982dc9595e49ce0f19332423b260045d0a6afe93cdbe2f1f624/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0f8aa1706812e00b9f19dfe0cdb3999b092ccb8ca168c0db5b8ea712456fd9b3", size = 363333771 }, + { url = "https://files.pythonhosted.org/packages/ae/71/1c91302526c45ab494c23f61c7a84aa568b8c1f9d196efa5993957faf906/nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:2fc8da60df463fdefa81e323eef2e36489e1c94335b5358bcb38360adf75ac9b", size = 363438805 }, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/b5/9fb3d00386d3361b03874246190dfec7b206fd74e6e287b26a8fcb359d95/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:79279b35cf6f91da114182a5ce1864997fd52294a87a16179ce275773799458a", size = 12354556 }, + { url = "https://files.pythonhosted.org/packages/67/42/f4f60238e8194a3106d06a058d494b18e006c10bb2b915655bd9f6ea4cb1/nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9dec60f5ac126f7bb551c055072b69d85392b13311fcc1bcda2202d172df30fb", size = 13813957 }, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/aa/083b01c427e963ad0b314040565ea396f914349914c298556484f799e61b/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0eedf14185e04b76aa05b1fea04133e59f465b6f960c0cbf4e37c3cb6b0ea198", size = 24133372 }, + { url = "https://files.pythonhosted.org/packages/2c/14/91ae57cd4db3f9ef7aa99f4019cfa8d54cb4caa7e00975df6467e9725a9f/nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a178759ebb095827bd30ef56598ec182b85547f1508941a3d560eb7ea1fbf338", size = 24640306 }, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/aa/b656d755f474e2084971e9a297def515938d56b466ab39624012070cb773/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:961fe0e2e716a2a1d967aab7caee97512f71767f852f67432d572e36cb3a11f3", size = 894177 }, + { url = "https://files.pythonhosted.org/packages/ea/27/1795d86fe88ef397885f2e580ac37628ed058a92ed2c39dc8eac3adf0619/nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:64403288fa2136ee8e467cdc9c9427e0434110899d07c779f25b5c068934faa5", size = 883737 }, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 }, +] + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.2.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/8a/0e728f749baca3fbeffad762738276e5df60851958be7783af121a7221e7/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399", size = 211422548 }, + { url = "https://files.pythonhosted.org/packages/27/94/3266821f65b92b3138631e9c8e7fe1fb513804ac934485a8d05776e1dd43/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f083fc24912aa410be21fa16d157fed2055dab1cc4b6934a0e03cba69eb242b9", size = 211459117 }, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.5.147" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/9c/a79180e4d70995fdf030c6946991d0171555c6edf95c265c6b2bf7011112/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1f173f09e3e3c76ab084aba0de819c49e56614feae5c12f69883f4ae9bb5fad9", size = 56314811 }, + { url = "https://files.pythonhosted.org/packages/8a/6d/44ad094874c6f1b9c654f8ed939590bdc408349f137f9b98a3a23ccec411/nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a88f583d4e0bb643c49743469964103aa59f7f708d862c3ddb0fc07f851e3b8b", size = 56305206 }, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.6.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/6b/a5c33cf16af09166845345275c34ad2190944bcc6026797a39f8e0a282e0/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e", size = 127634111 }, + { url = "https://files.pythonhosted.org/packages/3a/e1/5b9089a4b2a4790dfdea8b3a006052cfecff58139d5a4e34cb1a51df8d6f/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:19e33fa442bcfd085b3086c4ebf7e8debc07cfe01e11513cc6d332fd918ac260", size = 127936057 }, +] + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.3.1.170" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux') or (platform_system != 'Darwin' and platform_system != 'Linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/a9/c0d2f83a53d40a4a41be14cea6a0bf9e668ffcf8b004bd65633f433050c0/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3", size = 207381987 }, + { url = "https://files.pythonhosted.org/packages/db/f7/97a9ea26ed4bbbfc2d470994b8b4f338ef663be97b8f677519ac195e113d/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ea4f11a2904e2a8dc4b1833cc1b5181cde564edd0d5cd33e3c168eff2d1863f1", size = 207454763 }, +] + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/8e/675498726c605c9441cf46653bd29cb1b8666da1fb1469ffa25f67f20c58/nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:067a7f6d03ea0d4841c85f0c6f1991c5dda98211f6302cb83a4ab234ee95bef8", size = 149422781 }, + { url = "https://files.pythonhosted.org/packages/78/a8/bcbb63b53a4b1234feeafb65544ee55495e1bb37ec31b999b963cbccfd1d/nvidia_cusparselt_cu12-0.6.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:df2c24502fd76ebafe7457dbc4716b2fec071aabaed4fb7691a201cde03704d9", size = 150057751 }, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.21.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/99/12cd266d6233f47d00daf3a72739872bdc10267d0383508b0b9c84a18bb6/nvidia_nccl_cu12-2.21.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:8579076d30a8c24988834445f8d633c697d42397e92ffc3f63fa26766d25e0a0", size = 188654414 }, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/45/239d52c05074898a80a900f49b1615d81c07fceadd5ad6c4f86a987c0bc4/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4abe7fef64914ccfa909bc2ba39739670ecc9e820c83ccc7a6ed414122599b83", size = 20552510 }, + { url = "https://files.pythonhosted.org/packages/ff/ff/847841bacfbefc97a00036e0fce5a0f086b640756dc38caea5e1bb002655/nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57", size = 21066810 }, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.4.127" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/39/471f581edbb7804b39e8063d92fc8305bdc7a80ae5c07dbe6ea5c50d14a5/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7959ad635db13edf4fc65c06a6e9f9e55fc2f92596db928d169c0bb031e88ef3", size = 100417 }, + { url = "https://files.pythonhosted.org/packages/87/20/199b8713428322a2f22b722c62b8cc278cc53dffa9705d744484b5035ee9/nvidia_nvtx_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:781e950d9b9f60d8241ccea575b32f5105a5baf4c2351cab5256a24869f12a1a", size = 99144 }, +] + +[[package]] +name = "opencv-python" +version = "4.11.0.86" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/4d/53b30a2a3ac1f75f65a59eb29cf2ee7207ce64867db47036ad61743d5a23/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:432f67c223f1dc2824f5e73cdfcd9db0efc8710647d4e813012195dc9122a52a", size = 37326322 }, + { url = "https://files.pythonhosted.org/packages/3b/84/0a67490741867eacdfa37bc18df96e08a9d579583b419010d7f3da8ff503/opencv_python-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:9d05ef13d23fe97f575153558653e2d6e87103995d54e6a35db3f282fe1f9c66", size = 56723197 }, + { url = "https://files.pythonhosted.org/packages/f3/bd/29c126788da65c1fb2b5fb621b7fed0ed5f9122aa22a0868c5e2c15c6d23/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b92ae2c8852208817e6776ba1ea0d6b1e0a1b5431e971a2a0ddd2a8cc398202", size = 42230439 }, + { url = "https://files.pythonhosted.org/packages/2c/8b/90eb44a40476fa0e71e05a0283947cfd74a5d36121a11d926ad6f3193cc4/opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b02611523803495003bd87362db3e1d2a0454a6a63025dc6658a9830570aa0d", size = 62986597 }, + { url = "https://files.pythonhosted.org/packages/fb/d7/1d5941a9dde095468b288d989ff6539dd69cd429dbf1b9e839013d21b6f0/opencv_python-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:810549cb2a4aedaa84ad9a1c92fbfdfc14090e2749cedf2c1589ad8359aa169b", size = 29384337 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/f1c30a92854540bf789e9cd5dde7ef49bbe63f855b85a2e6b3db8135c591/opencv_python-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:085ad9b77c18853ea66283e98affefe2de8cc4c1f43eda4c100cf9b2721142ec", size = 39488044 }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, +] + +[[package]] +name = "peft" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accelerate" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/e1/aab80b861b4c18e85482940d504b3a7ee525bdf6ed93a1a2871881a180f1/peft-0.13.2.tar.gz", hash = "sha256:0e0cbd40ebdf5fe4ea79f255880d02f96712d18899509369a2cc5768ad46d672", size = 350390 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/9d/5f95bfb298c8d3b4e3a107701f9a4e7774a0d4d1f8eb0c9d5420b80f7c9d/peft-0.13.2-py3-none-any.whl", hash = "sha256:d4e0951ec78eac11c45a051801c569913436888c578d48e5ce86996b715bc6ef", size = 320731 }, +] + +[[package]] +name = "pillow" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/1c/2dcea34ac3d7bc96a1fd1bd0a6e06a57c67167fec2cff8d95d88229a8817/pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", size = 3229983 }, + { url = "https://files.pythonhosted.org/packages/14/ca/6bec3df25e4c88432681de94a3531cc738bd85dea6c7aa6ab6f81ad8bd11/pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", size = 3101831 }, + { url = "https://files.pythonhosted.org/packages/d4/2c/668e18e5521e46eb9667b09e501d8e07049eb5bfe39d56be0724a43117e6/pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", size = 4314074 }, + { url = "https://files.pythonhosted.org/packages/02/80/79f99b714f0fc25f6a8499ecfd1f810df12aec170ea1e32a4f75746051ce/pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", size = 4394933 }, + { url = "https://files.pythonhosted.org/packages/81/aa/8d4ad25dc11fd10a2001d5b8a80fdc0e564ac33b293bdfe04ed387e0fd95/pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", size = 4353349 }, + { url = "https://files.pythonhosted.org/packages/84/7a/cd0c3eaf4a28cb2a74bdd19129f7726277a7f30c4f8424cd27a62987d864/pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", size = 4476532 }, + { url = "https://files.pythonhosted.org/packages/8f/8b/a907fdd3ae8f01c7670dfb1499c53c28e217c338b47a813af8d815e7ce97/pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", size = 4279789 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/9f139d9e8cccd661c3efbf6898967a9a337eb2e9be2b454ba0a09533100d/pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", size = 4413131 }, + { url = "https://files.pythonhosted.org/packages/a8/68/0d8d461f42a3f37432203c8e6df94da10ac8081b6d35af1c203bf3111088/pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", size = 2291213 }, + { url = "https://files.pythonhosted.org/packages/14/81/d0dff759a74ba87715509af9f6cb21fa21d93b02b3316ed43bda83664db9/pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", size = 2625725 }, + { url = "https://files.pythonhosted.org/packages/ce/1f/8d50c096a1d58ef0584ddc37e6f602828515219e9d2428e14ce50f5ecad1/pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", size = 2375213 }, + { url = "https://files.pythonhosted.org/packages/dd/d6/2000bfd8d5414fb70cbbe52c8332f2283ff30ed66a9cde42716c8ecbe22c/pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", size = 3229968 }, + { url = "https://files.pythonhosted.org/packages/d9/45/3fe487010dd9ce0a06adf9b8ff4f273cc0a44536e234b0fad3532a42c15b/pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", size = 3101806 }, + { url = "https://files.pythonhosted.org/packages/e3/72/776b3629c47d9d5f1c160113158a7a7ad177688d3a1159cd3b62ded5a33a/pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", size = 4322283 }, + { url = "https://files.pythonhosted.org/packages/e4/c2/e25199e7e4e71d64eeb869f5b72c7ddec70e0a87926398785ab944d92375/pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070", size = 4402945 }, + { url = "https://files.pythonhosted.org/packages/c1/ed/51d6136c9d5911f78632b1b86c45241c712c5a80ed7fa7f9120a5dff1eba/pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6", size = 4361228 }, + { url = "https://files.pythonhosted.org/packages/48/a4/fbfe9d5581d7b111b28f1d8c2762dee92e9821bb209af9fa83c940e507a0/pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1", size = 4484021 }, + { url = "https://files.pythonhosted.org/packages/39/db/0b3c1a5018117f3c1d4df671fb8e47d08937f27519e8614bbe86153b65a5/pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2", size = 4287449 }, + { url = "https://files.pythonhosted.org/packages/d9/58/bc128da7fea8c89fc85e09f773c4901e95b5936000e6f303222490c052f3/pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96", size = 4419972 }, + { url = "https://files.pythonhosted.org/packages/5f/bb/58f34379bde9fe197f51841c5bbe8830c28bbb6d3801f16a83b8f2ad37df/pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", size = 2291201 }, + { url = "https://files.pythonhosted.org/packages/3a/c6/fce9255272bcf0c39e15abd2f8fd8429a954cf344469eaceb9d0d1366913/pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761", size = 2625686 }, + { url = "https://files.pythonhosted.org/packages/c8/52/8ba066d569d932365509054859f74f2a9abee273edcef5cd75e4bc3e831e/pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", size = 2375194 }, + { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 }, + { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 }, + { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 }, + { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 }, + { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 }, + { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 }, + { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 }, + { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 }, + { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 }, + { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 }, + { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, + { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, + { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, + { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, + { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, + { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, + { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, + { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, + { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, + { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, + { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, + { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, + { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, + { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, + { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, + { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, + { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, + { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, + { url = "https://files.pythonhosted.org/packages/fa/c5/389961578fb677b8b3244fcd934f720ed25a148b9a5cc81c91bdf59d8588/pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", size = 3198345 }, + { url = "https://files.pythonhosted.org/packages/c4/fa/803c0e50ffee74d4b965229e816af55276eac1d5806712de86f9371858fd/pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", size = 3072938 }, + { url = "https://files.pythonhosted.org/packages/dc/67/2a3a5f8012b5d8c63fe53958ba906c1b1d0482ebed5618057ef4d22f8076/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", size = 3400049 }, + { url = "https://files.pythonhosted.org/packages/e5/a0/514f0d317446c98c478d1872497eb92e7cde67003fed74f696441e647446/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", size = 3422431 }, + { url = "https://files.pythonhosted.org/packages/cd/00/20f40a935514037b7d3f87adfc87d2c538430ea625b63b3af8c3f5578e72/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", size = 3446208 }, + { url = "https://files.pythonhosted.org/packages/28/3c/7de681727963043e093c72e6c3348411b0185eab3263100d4490234ba2f6/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", size = 3509746 }, + { url = "https://files.pythonhosted.org/packages/41/67/936f9814bdd74b2dfd4822f1f7725ab5d8ff4103919a1664eb4874c58b2f/pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", size = 2626353 }, +] + +[[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 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, +] + +[[package]] +name = "pyarrow" +version = "20.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ee/a7810cb9f3d6e9238e61d312076a9859bf3668fd21c69744de9532383912/pyarrow-20.0.0.tar.gz", hash = "sha256:febc4a913592573c8d5805091a6c2b5064c8bd6e002131f01061797d91c783c1", size = 1125187 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/23/77094eb8ee0dbe88441689cb6afc40ac312a1e15d3a7acc0586999518222/pyarrow-20.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c7dd06fd7d7b410ca5dc839cc9d485d2bc4ae5240851bcd45d85105cc90a47d7", size = 30832591 }, + { url = "https://files.pythonhosted.org/packages/c3/d5/48cc573aff00d62913701d9fac478518f693b30c25f2c157550b0b2565cb/pyarrow-20.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d5382de8dc34c943249b01c19110783d0d64b207167c728461add1ecc2db88e4", size = 32273686 }, + { url = "https://files.pythonhosted.org/packages/37/df/4099b69a432b5cb412dd18adc2629975544d656df3d7fda6d73c5dba935d/pyarrow-20.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6415a0d0174487456ddc9beaead703d0ded5966129fa4fd3114d76b5d1c5ceae", size = 41337051 }, + { url = "https://files.pythonhosted.org/packages/4c/27/99922a9ac1c9226f346e3a1e15e63dee6f623ed757ff2893f9d6994a69d3/pyarrow-20.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15aa1b3b2587e74328a730457068dc6c89e6dcbf438d4369f572af9d320a25ee", size = 42404659 }, + { url = "https://files.pythonhosted.org/packages/21/d1/71d91b2791b829c9e98f1e0d85be66ed93aff399f80abb99678511847eaa/pyarrow-20.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5605919fbe67a7948c1f03b9f3727d82846c053cd2ce9303ace791855923fd20", size = 40695446 }, + { url = "https://files.pythonhosted.org/packages/f1/ca/ae10fba419a6e94329707487835ec721f5a95f3ac9168500bcf7aa3813c7/pyarrow-20.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a5704f29a74b81673d266e5ec1fe376f060627c2e42c5c7651288ed4b0db29e9", size = 42278528 }, + { url = "https://files.pythonhosted.org/packages/7a/a6/aba40a2bf01b5d00cf9cd16d427a5da1fad0fb69b514ce8c8292ab80e968/pyarrow-20.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:00138f79ee1b5aca81e2bdedb91e3739b987245e11fa3c826f9e57c5d102fb75", size = 42918162 }, + { url = "https://files.pythonhosted.org/packages/93/6b/98b39650cd64f32bf2ec6d627a9bd24fcb3e4e6ea1873c5e1ea8a83b1a18/pyarrow-20.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f2d67ac28f57a362f1a2c1e6fa98bfe2f03230f7e15927aecd067433b1e70ce8", size = 44550319 }, + { url = "https://files.pythonhosted.org/packages/ab/32/340238be1eb5037e7b5de7e640ee22334417239bc347eadefaf8c373936d/pyarrow-20.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:4a8b029a07956b8d7bd742ffca25374dd3f634b35e46cc7a7c3fa4c75b297191", size = 25770759 }, + { url = "https://files.pythonhosted.org/packages/47/a2/b7930824181ceadd0c63c1042d01fa4ef63eee233934826a7a2a9af6e463/pyarrow-20.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:24ca380585444cb2a31324c546a9a56abbe87e26069189e14bdba19c86c049f0", size = 30856035 }, + { url = "https://files.pythonhosted.org/packages/9b/18/c765770227d7f5bdfa8a69f64b49194352325c66a5c3bb5e332dfd5867d9/pyarrow-20.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:95b330059ddfdc591a3225f2d272123be26c8fa76e8c9ee1a77aad507361cfdb", size = 32309552 }, + { url = "https://files.pythonhosted.org/packages/44/fb/dfb2dfdd3e488bb14f822d7335653092dde150cffc2da97de6e7500681f9/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f0fb1041267e9968c6d0d2ce3ff92e3928b243e2b6d11eeb84d9ac547308232", size = 41334704 }, + { url = "https://files.pythonhosted.org/packages/58/0d/08a95878d38808051a953e887332d4a76bc06c6ee04351918ee1155407eb/pyarrow-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8ff87cc837601532cc8242d2f7e09b4e02404de1b797aee747dd4ba4bd6313f", size = 42399836 }, + { url = "https://files.pythonhosted.org/packages/f3/cd/efa271234dfe38f0271561086eedcad7bc0f2ddd1efba423916ff0883684/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7a3a5dcf54286e6141d5114522cf31dd67a9e7c9133d150799f30ee302a7a1ab", size = 40711789 }, + { url = "https://files.pythonhosted.org/packages/46/1f/7f02009bc7fc8955c391defee5348f510e589a020e4b40ca05edcb847854/pyarrow-20.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a6ad3e7758ecf559900261a4df985662df54fb7fdb55e8e3b3aa99b23d526b62", size = 42301124 }, + { url = "https://files.pythonhosted.org/packages/4f/92/692c562be4504c262089e86757a9048739fe1acb4024f92d39615e7bab3f/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6bb830757103a6cb300a04610e08d9636f0cd223d32f388418ea893a3e655f1c", size = 42916060 }, + { url = "https://files.pythonhosted.org/packages/a4/ec/9f5c7e7c828d8e0a3c7ef50ee62eca38a7de2fa6eb1b8fa43685c9414fef/pyarrow-20.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:96e37f0766ecb4514a899d9a3554fadda770fb57ddf42b63d80f14bc20aa7db3", size = 44547640 }, + { url = "https://files.pythonhosted.org/packages/54/96/46613131b4727f10fd2ffa6d0d6f02efcc09a0e7374eff3b5771548aa95b/pyarrow-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:3346babb516f4b6fd790da99b98bed9708e3f02e734c84971faccb20736848dc", size = 25781491 }, + { url = "https://files.pythonhosted.org/packages/a1/d6/0c10e0d54f6c13eb464ee9b67a68b8c71bcf2f67760ef5b6fbcddd2ab05f/pyarrow-20.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:75a51a5b0eef32727a247707d4755322cb970be7e935172b6a3a9f9ae98404ba", size = 30815067 }, + { url = "https://files.pythonhosted.org/packages/7e/e2/04e9874abe4094a06fd8b0cbb0f1312d8dd7d707f144c2ec1e5e8f452ffa/pyarrow-20.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:211d5e84cecc640c7a3ab900f930aaff5cd2702177e0d562d426fb7c4f737781", size = 32297128 }, + { url = "https://files.pythonhosted.org/packages/31/fd/c565e5dcc906a3b471a83273039cb75cb79aad4a2d4a12f76cc5ae90a4b8/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ba3cf4182828be7a896cbd232aa8dd6a31bd1f9e32776cc3796c012855e1199", size = 41334890 }, + { url = "https://files.pythonhosted.org/packages/af/a9/3bdd799e2c9b20c1ea6dc6fa8e83f29480a97711cf806e823f808c2316ac/pyarrow-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c3a01f313ffe27ac4126f4c2e5ea0f36a5fc6ab51f8726cf41fee4b256680bd", size = 42421775 }, + { url = "https://files.pythonhosted.org/packages/10/f7/da98ccd86354c332f593218101ae56568d5dcedb460e342000bd89c49cc1/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a2791f69ad72addd33510fec7bb14ee06c2a448e06b649e264c094c5b5f7ce28", size = 40687231 }, + { url = "https://files.pythonhosted.org/packages/bb/1b/2168d6050e52ff1e6cefc61d600723870bf569cbf41d13db939c8cf97a16/pyarrow-20.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4250e28a22302ce8692d3a0e8ec9d9dde54ec00d237cff4dfa9c1fbf79e472a8", size = 42295639 }, + { url = "https://files.pythonhosted.org/packages/b2/66/2d976c0c7158fd25591c8ca55aee026e6d5745a021915a1835578707feb3/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:89e030dc58fc760e4010148e6ff164d2f44441490280ef1e97a542375e41058e", size = 42908549 }, + { url = "https://files.pythonhosted.org/packages/31/a9/dfb999c2fc6911201dcbf348247f9cc382a8990f9ab45c12eabfd7243a38/pyarrow-20.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6102b4864d77102dbbb72965618e204e550135a940c2534711d5ffa787df2a5a", size = 44557216 }, + { url = "https://files.pythonhosted.org/packages/a0/8e/9adee63dfa3911be2382fb4d92e4b2e7d82610f9d9f668493bebaa2af50f/pyarrow-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:96d6a0a37d9c98be08f5ed6a10831d88d52cac7b13f5287f1e0f625a0de8062b", size = 25660496 }, + { url = "https://files.pythonhosted.org/packages/9b/aa/daa413b81446d20d4dad2944110dcf4cf4f4179ef7f685dd5a6d7570dc8e/pyarrow-20.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:a15532e77b94c61efadde86d10957950392999503b3616b2ffcef7621a002893", size = 30798501 }, + { url = "https://files.pythonhosted.org/packages/ff/75/2303d1caa410925de902d32ac215dc80a7ce7dd8dfe95358c165f2adf107/pyarrow-20.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:dd43f58037443af715f34f1322c782ec463a3c8a94a85fdb2d987ceb5658e061", size = 32277895 }, + { url = "https://files.pythonhosted.org/packages/92/41/fe18c7c0b38b20811b73d1bdd54b1fccba0dab0e51d2048878042d84afa8/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa0d288143a8585806e3cc7c39566407aab646fb9ece164609dac1cfff45f6ae", size = 41327322 }, + { url = "https://files.pythonhosted.org/packages/da/ab/7dbf3d11db67c72dbf36ae63dcbc9f30b866c153b3a22ef728523943eee6/pyarrow-20.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6953f0114f8d6f3d905d98e987d0924dabce59c3cda380bdfaa25a6201563b4", size = 42411441 }, + { url = "https://files.pythonhosted.org/packages/90/c3/0c7da7b6dac863af75b64e2f827e4742161128c350bfe7955b426484e226/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:991f85b48a8a5e839b2128590ce07611fae48a904cae6cab1f089c5955b57eb5", size = 40677027 }, + { url = "https://files.pythonhosted.org/packages/be/27/43a47fa0ff9053ab5203bb3faeec435d43c0d8bfa40179bfd076cdbd4e1c/pyarrow-20.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:97c8dc984ed09cb07d618d57d8d4b67a5100a30c3818c2fb0b04599f0da2de7b", size = 42281473 }, + { url = "https://files.pythonhosted.org/packages/bc/0b/d56c63b078876da81bbb9ba695a596eabee9b085555ed12bf6eb3b7cab0e/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9b71daf534f4745818f96c214dbc1e6124d7daf059167330b610fc69b6f3d3e3", size = 42893897 }, + { url = "https://files.pythonhosted.org/packages/92/ac/7d4bd020ba9145f354012838692d48300c1b8fe5634bfda886abcada67ed/pyarrow-20.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8b88758f9303fa5a83d6c90e176714b2fd3852e776fc2d7e42a22dd6c2fb368", size = 44543847 }, + { url = "https://files.pythonhosted.org/packages/9d/07/290f4abf9ca702c5df7b47739c1b2c83588641ddfa2cc75e34a301d42e55/pyarrow-20.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:30b3051b7975801c1e1d387e17c588d8ab05ced9b1e14eec57915f79869b5031", size = 25653219 }, + { url = "https://files.pythonhosted.org/packages/95/df/720bb17704b10bd69dde086e1400b8eefb8f58df3f8ac9cff6c425bf57f1/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:ca151afa4f9b7bc45bcc791eb9a89e90a9eb2772767d0b1e5389609c7d03db63", size = 30853957 }, + { url = "https://files.pythonhosted.org/packages/d9/72/0d5f875efc31baef742ba55a00a25213a19ea64d7176e0fe001c5d8b6e9a/pyarrow-20.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:4680f01ecd86e0dd63e39eb5cd59ef9ff24a9d166db328679e36c108dc993d4c", size = 32247972 }, + { url = "https://files.pythonhosted.org/packages/d5/bc/e48b4fa544d2eea72f7844180eb77f83f2030b84c8dad860f199f94307ed/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f4c8534e2ff059765647aa69b75d6543f9fef59e2cd4c6d18015192565d2b70", size = 41256434 }, + { url = "https://files.pythonhosted.org/packages/c3/01/974043a29874aa2cf4f87fb07fd108828fc7362300265a2a64a94965e35b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e1f8a47f4b4ae4c69c4d702cfbdfe4d41e18e5c7ef6f1bb1c50918c1e81c57b", size = 42353648 }, + { url = "https://files.pythonhosted.org/packages/68/95/cc0d3634cde9ca69b0e51cbe830d8915ea32dda2157560dda27ff3b3337b/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:a1f60dc14658efaa927f8214734f6a01a806d7690be4b3232ba526836d216122", size = 40619853 }, + { url = "https://files.pythonhosted.org/packages/29/c2/3ad40e07e96a3e74e7ed7cc8285aadfa84eb848a798c98ec0ad009eb6bcc/pyarrow-20.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:204a846dca751428991346976b914d6d2a82ae5b8316a6ed99789ebf976551e6", size = 42241743 }, + { url = "https://files.pythonhosted.org/packages/eb/cb/65fa110b483339add6a9bc7b6373614166b14e20375d4daa73483755f830/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f3b117b922af5e4c6b9a9115825726cac7d8b1421c37c2b5e24fbacc8930612c", size = 42839441 }, + { url = "https://files.pythonhosted.org/packages/98/7b/f30b1954589243207d7a0fbc9997401044bf9a033eec78f6cb50da3f304a/pyarrow-20.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e724a3fd23ae5b9c010e7be857f4405ed5e679db5c93e66204db1a69f733936a", size = 44503279 }, + { url = "https://files.pythonhosted.org/packages/37/40/ad395740cd641869a13bcf60851296c89624662575621968dcfafabaa7f6/pyarrow-20.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:82f1ee5133bd8f49d31be1299dc07f585136679666b502540db854968576faf9", size = 25944982 }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "(platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and 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/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, + { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, + { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, + { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, + { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, + { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, + { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, + { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, + { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, + { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, + { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, + { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, + { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, + { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, + { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669 }, + { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684 }, + { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589 }, + { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121 }, + { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275 }, + { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257 }, + { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727 }, + { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667 }, + { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963 }, + { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700 }, + { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592 }, + { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929 }, + { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213 }, + { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734 }, + { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052 }, + { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, + { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, + { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, + { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, + { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, + { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, + { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, + { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, + { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, + { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, + { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, + { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, + { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, + { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, + { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847 }, +] + +[[package]] +name = "ruff" +version = "0.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/3d/d9a195676f25d00dbfcf3cf95fdd4c685c497fcfa7e862a44ac5e4e96480/ruff-0.12.2.tar.gz", hash = "sha256:d7b4f55cd6f325cb7621244f19c873c565a08aff5a4ba9c69aa7355f3f7afd3e", size = 4432239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/b6/2098d0126d2d3318fd5bec3ad40d06c25d377d95749f7a0c5af17129b3b1/ruff-0.12.2-py3-none-linux_armv6l.whl", hash = "sha256:093ea2b221df1d2b8e7ad92fc6ffdca40a2cb10d8564477a987b44fd4008a7be", size = 10369761 }, + { url = "https://files.pythonhosted.org/packages/b1/4b/5da0142033dbe155dc598cfb99262d8ee2449d76920ea92c4eeb9547c208/ruff-0.12.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:09e4cf27cc10f96b1708100fa851e0daf21767e9709e1649175355280e0d950e", size = 11155659 }, + { url = "https://files.pythonhosted.org/packages/3e/21/967b82550a503d7c5c5c127d11c935344b35e8c521f52915fc858fb3e473/ruff-0.12.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ae64755b22f4ff85e9c52d1f82644abd0b6b6b6deedceb74bd71f35c24044cc", size = 10537769 }, + { url = "https://files.pythonhosted.org/packages/33/91/00cff7102e2ec71a4890fb7ba1803f2cdb122d82787c7d7cf8041fe8cbc1/ruff-0.12.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eb3a6b2db4d6e2c77e682f0b988d4d61aff06860158fdb413118ca133d57922", size = 10717602 }, + { url = "https://files.pythonhosted.org/packages/9b/eb/928814daec4e1ba9115858adcda44a637fb9010618721937491e4e2283b8/ruff-0.12.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73448de992d05517170fc37169cbca857dfeaeaa8c2b9be494d7bcb0d36c8f4b", size = 10198772 }, + { url = "https://files.pythonhosted.org/packages/50/fa/f15089bc20c40f4f72334f9145dde55ab2b680e51afb3b55422effbf2fb6/ruff-0.12.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b94317cbc2ae4a2771af641739f933934b03555e51515e6e021c64441532d", size = 11845173 }, + { url = "https://files.pythonhosted.org/packages/43/9f/1f6f98f39f2b9302acc161a4a2187b1e3a97634fe918a8e731e591841cf4/ruff-0.12.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:45fc42c3bf1d30d2008023a0a9a0cfb06bf9835b147f11fe0679f21ae86d34b1", size = 12553002 }, + { url = "https://files.pythonhosted.org/packages/d8/70/08991ac46e38ddd231c8f4fd05ef189b1b94be8883e8c0c146a025c20a19/ruff-0.12.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce48f675c394c37e958bf229fb5c1e843e20945a6d962cf3ea20b7a107dcd9f4", size = 12171330 }, + { url = "https://files.pythonhosted.org/packages/88/a9/5a55266fec474acfd0a1c73285f19dd22461d95a538f29bba02edd07a5d9/ruff-0.12.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793d8859445ea47591272021a81391350205a4af65a9392401f418a95dfb75c9", size = 11774717 }, + { url = "https://files.pythonhosted.org/packages/87/e5/0c270e458fc73c46c0d0f7cf970bb14786e5fdb88c87b5e423a4bd65232b/ruff-0.12.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6932323db80484dda89153da3d8e58164d01d6da86857c79f1961934354992da", size = 11646659 }, + { url = "https://files.pythonhosted.org/packages/b7/b6/45ab96070c9752af37f0be364d849ed70e9ccede07675b0ec4e3ef76b63b/ruff-0.12.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6aa7e623a3a11538108f61e859ebf016c4f14a7e6e4eba1980190cacb57714ce", size = 10604012 }, + { url = "https://files.pythonhosted.org/packages/86/91/26a6e6a424eb147cc7627eebae095cfa0b4b337a7c1c413c447c9ebb72fd/ruff-0.12.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2a4a20aeed74671b2def096bdf2eac610c7d8ffcbf4fb0e627c06947a1d7078d", size = 10176799 }, + { url = "https://files.pythonhosted.org/packages/f5/0c/9f344583465a61c8918a7cda604226e77b2c548daf8ef7c2bfccf2b37200/ruff-0.12.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:71a4c550195612f486c9d1f2b045a600aeba851b298c667807ae933478fcef04", size = 11241507 }, + { url = "https://files.pythonhosted.org/packages/1c/b7/99c34ded8fb5f86c0280278fa89a0066c3760edc326e935ce0b1550d315d/ruff-0.12.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4987b8f4ceadf597c927beee65a5eaf994c6e2b631df963f86d8ad1bdea99342", size = 11717609 }, + { url = "https://files.pythonhosted.org/packages/51/de/8589fa724590faa057e5a6d171e7f2f6cffe3287406ef40e49c682c07d89/ruff-0.12.2-py3-none-win32.whl", hash = "sha256:369ffb69b70cd55b6c3fc453b9492d98aed98062db9fec828cdfd069555f5f1a", size = 10523823 }, + { url = "https://files.pythonhosted.org/packages/94/47/8abf129102ae4c90cba0c2199a1a9b0fa896f6f806238d6f8c14448cc748/ruff-0.12.2-py3-none-win_amd64.whl", hash = "sha256:dca8a3b6d6dc9810ed8f328d406516bf4d660c00caeaef36eb831cf4871b0639", size = 11629831 }, + { url = "https://files.pythonhosted.org/packages/e2/1f/72d2946e3cc7456bb837e88000eb3437e55f80db339c840c04015a11115d/ruff-0.12.2-py3-none-win_arm64.whl", hash = "sha256:48d6c6bfb4761df68bc05ae630e24f506755e702d4fb08f08460be778c7ccb12", size = 10735334 }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493 }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400 }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891 }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694 }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642 }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241 }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001 }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013 }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687 }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147 }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, +] + +[[package]] +name = "scipy" +version = "1.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/b9/31ba9cd990e626574baf93fbc1ac61cf9ed54faafd04c479117517661637/scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec", size = 59417316 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/df/ef233fff6838fe6f7840d69b5ef9f20d2b5c912a8727b21ebf876cb15d54/scipy-1.15.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a2ec871edaa863e8213ea5df811cd600734f6400b4af272e1c011e69401218e9", size = 38692502 }, + { url = "https://files.pythonhosted.org/packages/5c/20/acdd4efb8a68b842968f7bc5611b1aeb819794508771ad104de418701422/scipy-1.15.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6f223753c6ea76983af380787611ae1291e3ceb23917393079dcc746ba60cfb5", size = 30085508 }, + { url = "https://files.pythonhosted.org/packages/42/55/39cf96ca7126f1e78ee72a6344ebdc6702fc47d037319ad93221063e6cf4/scipy-1.15.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ecf797d2d798cf7c838c6d98321061eb3e72a74710e6c40540f0e8087e3b499e", size = 22359166 }, + { url = "https://files.pythonhosted.org/packages/51/48/708d26a4ab8a1441536bf2dfcad1df0ca14a69f010fba3ccbdfc02df7185/scipy-1.15.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:9b18aa747da280664642997e65aab1dd19d0c3d17068a04b3fe34e2559196cb9", size = 25112047 }, + { url = "https://files.pythonhosted.org/packages/dd/65/f9c5755b995ad892020381b8ae11f16d18616208e388621dfacc11df6de6/scipy-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87994da02e73549dfecaed9e09a4f9d58a045a053865679aeb8d6d43747d4df3", size = 35536214 }, + { url = "https://files.pythonhosted.org/packages/de/3c/c96d904b9892beec978562f64d8cc43f9cca0842e65bd3cd1b7f7389b0ba/scipy-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69ea6e56d00977f355c0f84eba69877b6df084516c602d93a33812aa04d90a3d", size = 37646981 }, + { url = "https://files.pythonhosted.org/packages/3d/74/c2d8a24d18acdeae69ed02e132b9bc1bb67b7bee90feee1afe05a68f9d67/scipy-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:888307125ea0c4466287191e5606a2c910963405ce9671448ff9c81c53f85f58", size = 37230048 }, + { url = "https://files.pythonhosted.org/packages/42/19/0aa4ce80eca82d487987eff0bc754f014dec10d20de2f66754fa4ea70204/scipy-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9412f5e408b397ff5641080ed1e798623dbe1ec0d78e72c9eca8992976fa65aa", size = 40010322 }, + { url = "https://files.pythonhosted.org/packages/d0/d2/f0683b7e992be44d1475cc144d1f1eeae63c73a14f862974b4db64af635e/scipy-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:b5e025e903b4f166ea03b109bb241355b9c42c279ea694d8864d033727205e65", size = 41233385 }, + { url = "https://files.pythonhosted.org/packages/40/1f/bf0a5f338bda7c35c08b4ed0df797e7bafe8a78a97275e9f439aceb46193/scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4", size = 38703651 }, + { url = "https://files.pythonhosted.org/packages/de/54/db126aad3874601048c2c20ae3d8a433dbfd7ba8381551e6f62606d9bd8e/scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1", size = 30102038 }, + { url = "https://files.pythonhosted.org/packages/61/d8/84da3fffefb6c7d5a16968fe5b9f24c98606b165bb801bb0b8bc3985200f/scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971", size = 22375518 }, + { url = "https://files.pythonhosted.org/packages/44/78/25535a6e63d3b9c4c90147371aedb5d04c72f3aee3a34451f2dc27c0c07f/scipy-1.15.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8bf5cb4a25046ac61d38f8d3c3426ec11ebc350246a4642f2f315fe95bda655", size = 25142523 }, + { url = "https://files.pythonhosted.org/packages/e0/22/4b4a26fe1cd9ed0bc2b2cb87b17d57e32ab72c346949eaf9288001f8aa8e/scipy-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a8e34cf4c188b6dd004654f88586d78f95639e48a25dfae9c5e34a6dc34547e", size = 35491547 }, + { url = "https://files.pythonhosted.org/packages/32/ea/564bacc26b676c06a00266a3f25fdfe91a9d9a2532ccea7ce6dd394541bc/scipy-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0d2c2075946346e4408b211240764759e0fabaeb08d871639b5f3b1aca8a0", size = 37634077 }, + { url = "https://files.pythonhosted.org/packages/43/c2/bfd4e60668897a303b0ffb7191e965a5da4056f0d98acfb6ba529678f0fb/scipy-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:42dabaaa798e987c425ed76062794e93a243be8f0f20fff6e7a89f4d61cb3d40", size = 37231657 }, + { url = "https://files.pythonhosted.org/packages/4a/75/5f13050bf4f84c931bcab4f4e83c212a36876c3c2244475db34e4b5fe1a6/scipy-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5e296ec63c5da6ba6fa0343ea73fd51b8b3e1a300b0a8cae3ed4b1122c7462", size = 40035857 }, + { url = "https://files.pythonhosted.org/packages/b9/8b/7ec1832b09dbc88f3db411f8cdd47db04505c4b72c99b11c920a8f0479c3/scipy-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:597a0c7008b21c035831c39927406c6181bcf8f60a73f36219b69d010aa04737", size = 41217654 }, + { url = "https://files.pythonhosted.org/packages/4b/5d/3c78815cbab499610f26b5bae6aed33e227225a9fa5290008a733a64f6fc/scipy-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c4697a10da8f8765bb7c83e24a470da5797e37041edfd77fd95ba3811a47c4fd", size = 38756184 }, + { url = "https://files.pythonhosted.org/packages/37/20/3d04eb066b471b6e171827548b9ddb3c21c6bbea72a4d84fc5989933910b/scipy-1.15.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:869269b767d5ee7ea6991ed7e22b3ca1f22de73ab9a49c44bad338b725603301", size = 30163558 }, + { url = "https://files.pythonhosted.org/packages/a4/98/e5c964526c929ef1f795d4c343b2ff98634ad2051bd2bbadfef9e772e413/scipy-1.15.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bad78d580270a4d32470563ea86c6590b465cb98f83d760ff5b0990cb5518a93", size = 22437211 }, + { url = "https://files.pythonhosted.org/packages/1d/cd/1dc7371e29195ecbf5222f9afeedb210e0a75057d8afbd942aa6cf8c8eca/scipy-1.15.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b09ae80010f52efddb15551025f9016c910296cf70adbf03ce2a8704f3a5ad20", size = 25232260 }, + { url = "https://files.pythonhosted.org/packages/f0/24/1a181a9e5050090e0b5138c5f496fee33293c342b788d02586bc410c6477/scipy-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6fd6eac1ce74a9f77a7fc724080d507c5812d61e72bd5e4c489b042455865e", size = 35198095 }, + { url = "https://files.pythonhosted.org/packages/c0/53/eaada1a414c026673eb983f8b4a55fe5eb172725d33d62c1b21f63ff6ca4/scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8", size = 37297371 }, + { url = "https://files.pythonhosted.org/packages/e9/06/0449b744892ed22b7e7b9a1994a866e64895363572677a316a9042af1fe5/scipy-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:03205d57a28e18dfd39f0377d5002725bf1f19a46f444108c29bdb246b6c8a11", size = 36872390 }, + { url = "https://files.pythonhosted.org/packages/6a/6f/a8ac3cfd9505ec695c1bc35edc034d13afbd2fc1882a7c6b473e280397bb/scipy-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:601881dfb761311045b03114c5fe718a12634e5608c3b403737ae463c9885d53", size = 39700276 }, + { url = "https://files.pythonhosted.org/packages/f5/6f/e6e5aff77ea2a48dd96808bb51d7450875af154ee7cbe72188afb0b37929/scipy-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c68b6a43259ba0aab737237876e5c2c549a031ddb7abc28c7b47f22e202ded", size = 40942317 }, + { url = "https://files.pythonhosted.org/packages/53/40/09319f6e0f276ea2754196185f95cd191cb852288440ce035d5c3a931ea2/scipy-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01edfac9f0798ad6b46d9c4c9ca0e0ad23dbf0b1eb70e96adb9fa7f525eff0bf", size = 38717587 }, + { url = "https://files.pythonhosted.org/packages/fe/c3/2854f40ecd19585d65afaef601e5e1f8dbf6758b2f95b5ea93d38655a2c6/scipy-1.15.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:08b57a9336b8e79b305a143c3655cc5bdbe6d5ece3378578888d2afbb51c4e37", size = 30100266 }, + { url = "https://files.pythonhosted.org/packages/dd/b1/f9fe6e3c828cb5930b5fe74cb479de5f3d66d682fa8adb77249acaf545b8/scipy-1.15.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:54c462098484e7466362a9f1672d20888f724911a74c22ae35b61f9c5919183d", size = 22373768 }, + { url = "https://files.pythonhosted.org/packages/15/9d/a60db8c795700414c3f681908a2b911e031e024d93214f2d23c6dae174ab/scipy-1.15.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:cf72ff559a53a6a6d77bd8eefd12a17995ffa44ad86c77a5df96f533d4e6c6bb", size = 25154719 }, + { url = "https://files.pythonhosted.org/packages/37/3b/9bda92a85cd93f19f9ed90ade84aa1e51657e29988317fabdd44544f1dd4/scipy-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9de9d1416b3d9e7df9923ab23cd2fe714244af10b763975bea9e4f2e81cebd27", size = 35163195 }, + { url = "https://files.pythonhosted.org/packages/03/5a/fc34bf1aa14dc7c0e701691fa8685f3faec80e57d816615e3625f28feb43/scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0", size = 37255404 }, + { url = "https://files.pythonhosted.org/packages/4a/71/472eac45440cee134c8a180dbe4c01b3ec247e0338b7c759e6cd71f199a7/scipy-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5ea7ed46d437fc52350b028b1d44e002646e28f3e8ddc714011aaf87330f2f32", size = 36860011 }, + { url = "https://files.pythonhosted.org/packages/01/b3/21f890f4f42daf20e4d3aaa18182dddb9192771cd47445aaae2e318f6738/scipy-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11e7ad32cf184b74380f43d3c0a706f49358b904fa7d5345f16ddf993609184d", size = 39657406 }, + { url = "https://files.pythonhosted.org/packages/0d/76/77cf2ac1f2a9cc00c073d49e1e16244e389dd88e2490c91d84e1e3e4d126/scipy-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:a5080a79dfb9b78b768cebf3c9dcbc7b665c5875793569f48bf0e2b1d7f68f6f", size = 40961243 }, + { url = "https://files.pythonhosted.org/packages/4c/4b/a57f8ddcf48e129e6054fa9899a2a86d1fc6b07a0e15c7eebff7ca94533f/scipy-1.15.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:447ce30cee6a9d5d1379087c9e474628dab3db4a67484be1b7dc3196bfb2fac9", size = 38870286 }, + { url = "https://files.pythonhosted.org/packages/0c/43/c304d69a56c91ad5f188c0714f6a97b9c1fed93128c691148621274a3a68/scipy-1.15.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c90ebe8aaa4397eaefa8455a8182b164a6cc1d59ad53f79943f266d99f68687f", size = 30141634 }, + { url = "https://files.pythonhosted.org/packages/44/1a/6c21b45d2548eb73be9b9bff421aaaa7e85e22c1f9b3bc44b23485dfce0a/scipy-1.15.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:def751dd08243934c884a3221156d63e15234a3155cf25978b0a668409d45eb6", size = 22415179 }, + { url = "https://files.pythonhosted.org/packages/74/4b/aefac4bba80ef815b64f55da06f62f92be5d03b467f2ce3668071799429a/scipy-1.15.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:302093e7dfb120e55515936cb55618ee0b895f8bcaf18ff81eca086c17bd80af", size = 25126412 }, + { url = "https://files.pythonhosted.org/packages/b1/53/1cbb148e6e8f1660aacd9f0a9dfa2b05e9ff1cb54b4386fe868477972ac2/scipy-1.15.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd5b77413e1855351cdde594eca99c1f4a588c2d63711388b6a1f1c01f62274", size = 34952867 }, + { url = "https://files.pythonhosted.org/packages/2c/23/e0eb7f31a9c13cf2dca083828b97992dd22f8184c6ce4fec5deec0c81fcf/scipy-1.15.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d0194c37037707b2afa7a2f2a924cf7bac3dc292d51b6a925e5fcb89bc5c776", size = 36890009 }, + { url = "https://files.pythonhosted.org/packages/03/f3/e699e19cabe96bbac5189c04aaa970718f0105cff03d458dc5e2b6bd1e8c/scipy-1.15.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:bae43364d600fdc3ac327db99659dcb79e6e7ecd279a75fe1266669d9a652828", size = 36545159 }, + { url = "https://files.pythonhosted.org/packages/af/f5/ab3838e56fe5cc22383d6fcf2336e48c8fe33e944b9037fbf6cbdf5a11f8/scipy-1.15.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f031846580d9acccd0044efd1a90e6f4df3a6e12b4b6bd694a7bc03a89892b28", size = 39136566 }, + { url = "https://files.pythonhosted.org/packages/0a/c8/b3f566db71461cabd4b2d5b39bcc24a7e1c119535c8361f81426be39bb47/scipy-1.15.2-cp313-cp313t-win_amd64.whl", hash = "sha256:fe8a9eb875d430d81755472c5ba75e84acc980e4a8f6204d402849234d3017db", size = 40477705 }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486 }, +] + +[[package]] +name = "soundfile" +version = "0.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/41/9b873a8c055582859b239be17902a85339bec6a30ad162f98c9b0288a2cc/soundfile-0.13.1.tar.gz", hash = "sha256:b2c68dab1e30297317080a5b43df57e302584c49e2942defdde0acccc53f0e5b", size = 46156 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/28/e2a36573ccbcf3d57c00626a21fe51989380636e821b341d36ccca0c1c3a/soundfile-0.13.1-py2.py3-none-any.whl", hash = "sha256:a23c717560da2cf4c7b5ae1142514e0fd82d6bbd9dfc93a50423447142f2c445", size = 25751 }, + { url = "https://files.pythonhosted.org/packages/ea/ab/73e97a5b3cc46bba7ff8650a1504348fa1863a6f9d57d7001c6b67c5f20e/soundfile-0.13.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:82dc664d19831933fe59adad199bf3945ad06d84bc111a5b4c0d3089a5b9ec33", size = 1142250 }, + { url = "https://files.pythonhosted.org/packages/a0/e5/58fd1a8d7b26fc113af244f966ee3aecf03cb9293cb935daaddc1e455e18/soundfile-0.13.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:743f12c12c4054921e15736c6be09ac26b3b3d603aef6fd69f9dde68748f2593", size = 1101406 }, + { url = "https://files.pythonhosted.org/packages/58/ae/c0e4a53d77cf6e9a04179535766b3321b0b9ced5f70522e4caf9329f0046/soundfile-0.13.1-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9c9e855f5a4d06ce4213f31918653ab7de0c5a8d8107cd2427e44b42df547deb", size = 1235729 }, + { url = "https://files.pythonhosted.org/packages/57/5e/70bdd9579b35003a489fc850b5047beeda26328053ebadc1fb60f320f7db/soundfile-0.13.1-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:03267c4e493315294834a0870f31dbb3b28a95561b80b134f0bd3cf2d5f0e618", size = 1313646 }, + { url = "https://files.pythonhosted.org/packages/fe/df/8c11dc4dfceda14e3003bb81a0d0edcaaf0796dd7b4f826ea3e532146bba/soundfile-0.13.1-py2.py3-none-win32.whl", hash = "sha256:c734564fab7c5ddf8e9be5bf70bab68042cd17e9c214c06e365e20d64f9a69d5", size = 899881 }, + { url = "https://files.pythonhosted.org/packages/14/e9/6b761de83277f2f02ded7e7ea6f07828ec78e4b229b80e4ca55dd205b9dc/soundfile-0.13.1-py2.py3-none-win_amd64.whl", hash = "sha256:1e70a05a0626524a69e9f0f4dd2ec174b4e9567f4d8b6c11d38b5c289be36ee9", size = 1019162 }, +] + +[[package]] +name = "sympy" +version = "1.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/2d/b0fce2b8201635f60e8c95990080f58461cc9ca3d5026de2e900f38a7f21/tokenizers-0.21.2.tar.gz", hash = "sha256:fdc7cffde3e2113ba0e6cc7318c40e3438a4d74bbc62bf04bcc63bdfb082ac77", size = 351545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/cc/2936e2d45ceb130a21d929743f1e9897514691bec123203e10837972296f/tokenizers-0.21.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:342b5dfb75009f2255ab8dec0041287260fed5ce00c323eb6bab639066fef8ec", size = 2875206 }, + { url = "https://files.pythonhosted.org/packages/6c/e6/33f41f2cc7861faeba8988e7a77601407bf1d9d28fc79c5903f8f77df587/tokenizers-0.21.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:126df3205d6f3a93fea80c7a8a266a78c1bd8dd2fe043386bafdd7736a23e45f", size = 2732655 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1791eb329c07122a75b01035b1a3aa22ad139f3ce0ece1b059b506d9d9de/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a32cd81be21168bd0d6a0f0962d60177c447a1aa1b1e48fa6ec9fc728ee0b12", size = 3019202 }, + { url = "https://files.pythonhosted.org/packages/05/15/fd2d8104faa9f86ac68748e6f7ece0b5eb7983c7efc3a2c197cb98c99030/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8bd8999538c405133c2ab999b83b17c08b7fc1b48c1ada2469964605a709ef91", size = 2934539 }, + { url = "https://files.pythonhosted.org/packages/a5/2e/53e8fd053e1f3ffbe579ca5f9546f35ac67cf0039ed357ad7ec57f5f5af0/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e9944e61239b083a41cf8fc42802f855e1dca0f499196df37a8ce219abac6eb", size = 3248665 }, + { url = "https://files.pythonhosted.org/packages/00/15/79713359f4037aa8f4d1f06ffca35312ac83629da062670e8830917e2153/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:514cd43045c5d546f01142ff9c79a96ea69e4b5cda09e3027708cb2e6d5762ab", size = 3451305 }, + { url = "https://files.pythonhosted.org/packages/38/5f/959f3a8756fc9396aeb704292777b84f02a5c6f25c3fc3ba7530db5feb2c/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1b9405822527ec1e0f7d8d2fdb287a5730c3a6518189c968254a8441b21faae", size = 3214757 }, + { url = "https://files.pythonhosted.org/packages/c5/74/f41a432a0733f61f3d21b288de6dfa78f7acff309c6f0f323b2833e9189f/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed9a4d51c395103ad24f8e7eb976811c57fbec2af9f133df471afcd922e5020", size = 3121887 }, + { url = "https://files.pythonhosted.org/packages/3c/6a/bc220a11a17e5d07b0dfb3b5c628621d4dcc084bccd27cfaead659963016/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c41862df3d873665ec78b6be36fcc30a26e3d4902e9dd8608ed61d49a48bc19", size = 9091965 }, + { url = "https://files.pythonhosted.org/packages/6c/bd/ac386d79c4ef20dc6f39c4706640c24823dca7ebb6f703bfe6b5f0292d88/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed21dc7e624e4220e21758b2e62893be7101453525e3d23264081c9ef9a6d00d", size = 9053372 }, + { url = "https://files.pythonhosted.org/packages/63/7b/5440bf203b2a5358f074408f7f9c42884849cd9972879e10ee6b7a8c3b3d/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:0e73770507e65a0e0e2a1affd6b03c36e3bc4377bd10c9ccf51a82c77c0fe365", size = 9298632 }, + { url = "https://files.pythonhosted.org/packages/a4/d2/faa1acac3f96a7427866e94ed4289949b2524f0c1878512516567d80563c/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:106746e8aa9014a12109e58d540ad5465b4c183768ea96c03cbc24c44d329958", size = 9470074 }, + { url = "https://files.pythonhosted.org/packages/d8/a5/896e1ef0707212745ae9f37e84c7d50269411aef2e9ccd0de63623feecdf/tokenizers-0.21.2-cp39-abi3-win32.whl", hash = "sha256:cabda5a6d15d620b6dfe711e1af52205266d05b379ea85a8a301b3593c60e962", size = 2330115 }, + { url = "https://files.pythonhosted.org/packages/13/c3/cc2755ee10be859c4338c962a35b9a663788c0c0b50c0bdd8078fb6870cf/tokenizers-0.21.2-cp39-abi3-win_amd64.whl", hash = "sha256:58747bb898acdb1007f37a7bbe614346e98dc28708ffb66a3fd50ce169ac6c98", size = 2509918 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "torch" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "setuptools", marker = "python_full_version >= '3.12'" }, + { name = "sympy" }, + { name = "triton", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/81/aa9ab58ec10264c1abe62c8b73f5086c3c558885d6beecebf699f0dbeaeb/torch-2.6.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:6860df13d9911ac158f4c44031609700e1eba07916fff62e21e6ffa0a9e01961", size = 766685561 }, + { url = "https://files.pythonhosted.org/packages/86/86/e661e229df2f5bfc6eab4c97deb1286d598bbeff31ab0cdb99b3c0d53c6f/torch-2.6.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c4f103a49830ce4c7561ef4434cc7926e5a5fe4e5eb100c19ab36ea1e2b634ab", size = 95751887 }, + { url = "https://files.pythonhosted.org/packages/20/e0/5cb2f8493571f0a5a7273cd7078f191ac252a402b5fb9cb6091f14879109/torch-2.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:56eeaf2ecac90da5d9e35f7f35eb286da82673ec3c582e310a8d1631a1c02341", size = 204165139 }, + { url = "https://files.pythonhosted.org/packages/e5/16/ea1b7842413a7b8a5aaa5e99e8eaf3da3183cc3ab345ad025a07ff636301/torch-2.6.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:09e06f9949e1a0518c5b09fe95295bc9661f219d9ecb6f9893e5123e10696628", size = 66520221 }, + { url = "https://files.pythonhosted.org/packages/78/a9/97cbbc97002fff0de394a2da2cdfa859481fdca36996d7bd845d50aa9d8d/torch-2.6.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7979834102cd5b7a43cc64e87f2f3b14bd0e1458f06e9f88ffa386d07c7446e1", size = 766715424 }, + { url = "https://files.pythonhosted.org/packages/6d/fa/134ce8f8a7ea07f09588c9cc2cea0d69249efab977707cf67669431dcf5c/torch-2.6.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:ccbd0320411fe1a3b3fec7b4d3185aa7d0c52adac94480ab024b5c8f74a0bf1d", size = 95759416 }, + { url = "https://files.pythonhosted.org/packages/11/c5/2370d96b31eb1841c3a0883a492c15278a6718ccad61bb6a649c80d1d9eb/torch-2.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:46763dcb051180ce1ed23d1891d9b1598e07d051ce4c9d14307029809c4d64f7", size = 204164970 }, + { url = "https://files.pythonhosted.org/packages/0b/fa/f33a4148c6fb46ca2a3f8de39c24d473822d5774d652b66ed9b1214da5f7/torch-2.6.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:94fc63b3b4bedd327af588696559f68c264440e2503cc9e6954019473d74ae21", size = 66530713 }, + { url = "https://files.pythonhosted.org/packages/e5/35/0c52d708144c2deb595cd22819a609f78fdd699b95ff6f0ebcd456e3c7c1/torch-2.6.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:2bb8987f3bb1ef2675897034402373ddfc8f5ef0e156e2d8cfc47cacafdda4a9", size = 766624563 }, + { url = "https://files.pythonhosted.org/packages/01/d6/455ab3fbb2c61c71c8842753b566012e1ed111e7a4c82e0e1c20d0c76b62/torch-2.6.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b789069020c5588c70d5c2158ac0aa23fd24a028f34a8b4fcb8fcb4d7efcf5fb", size = 95607867 }, + { url = "https://files.pythonhosted.org/packages/18/cf/ae99bd066571656185be0d88ee70abc58467b76f2f7c8bfeb48735a71fe6/torch-2.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7e1448426d0ba3620408218b50aa6ada88aeae34f7a239ba5431f6c8774b1239", size = 204120469 }, + { url = "https://files.pythonhosted.org/packages/81/b4/605ae4173aa37fb5aa14605d100ff31f4f5d49f617928c9f486bb3aaec08/torch-2.6.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:9a610afe216a85a8b9bc9f8365ed561535c93e804c2a317ef7fabcc5deda0989", size = 66532538 }, + { url = "https://files.pythonhosted.org/packages/24/85/ead1349fc30fe5a32cadd947c91bda4a62fbfd7f8c34ee61f6398d38fb48/torch-2.6.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:4874a73507a300a5d089ceaff616a569e7bb7c613c56f37f63ec3ffac65259cf", size = 766626191 }, + { url = "https://files.pythonhosted.org/packages/dd/b0/26f06f9428b250d856f6d512413e9e800b78625f63801cbba13957432036/torch-2.6.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a0d5e1b9874c1a6c25556840ab8920569a7a4137afa8a63a32cee0bc7d89bd4b", size = 95611439 }, + { url = "https://files.pythonhosted.org/packages/c2/9c/fc5224e9770c83faed3a087112d73147cd7c7bfb7557dcf9ad87e1dda163/torch-2.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:510c73251bee9ba02ae1cb6c9d4ee0907b3ce6020e62784e2d7598e0cfa4d6cc", size = 204126475 }, + { url = "https://files.pythonhosted.org/packages/88/8b/d60c0491ab63634763be1537ad488694d316ddc4a20eaadd639cedc53971/torch-2.6.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:ff96f4038f8af9f7ec4231710ed4549da1bdebad95923953a25045dcf6fd87e2", size = 66536783 }, +] + +[[package]] +name = "torchvision" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/20/72eb0b5b08fa293f20fc41c374e37cf899f0033076f0144d2cdc48f9faee/torchvision-0.21.0-1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5568c5a1ff1b2ec33127b629403adb530fab81378d9018ca4ed6508293f76e2b", size = 2327643 }, + { url = "https://files.pythonhosted.org/packages/4e/3d/b7241abfa3e6651c6e00796f5de2bd1ce4d500bf5159bcbfeea47e711b93/torchvision-0.21.0-1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:ff96666b94a55e802ea6796cabe788541719e6f4905fc59c380fed3517b6a64d", size = 2329320 }, + { url = "https://files.pythonhosted.org/packages/52/5b/76ca113a853b19c7b1da761f8a72cb6429b3bd0bf932537d8df4657f47c3/torchvision-0.21.0-1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ffa2a16499508fe6798323e455f312c7c55f2a88901c9a7c0fb1efa86cf7e327", size = 2329878 }, + { url = "https://files.pythonhosted.org/packages/4e/fe/5e193353706dab96fe73ae100d5a633ff635ce310e0d92f3bc2958d075b1/torchvision-0.21.0-1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7e9e9afa150e40cd2a8f0701c43cb82a8d724f512896455c0918b987f94b84a4", size = 2280711 }, + { url = "https://files.pythonhosted.org/packages/8e/0d/143bd264876fad17c82096b6c2d433f1ac9b29cdc69ee45023096976ee3d/torchvision-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:044ea420b8c6c3162a234cada8e2025b9076fa82504758cd11ec5d0f8cd9fa37", size = 1784140 }, + { url = "https://files.pythonhosted.org/packages/5e/44/32e2d2d174391374d5ff3c4691b802e8efda9ae27ab9062eca2255b006af/torchvision-0.21.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:b0c0b264b89ab572888244f2e0bad5b7eaf5b696068fc0b93e96f7c3c198953f", size = 7237187 }, + { url = "https://files.pythonhosted.org/packages/0e/6b/4fca9373eda42c1b04096758306b7bd55f7d8f78ba273446490855a0f25d/torchvision-0.21.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:54815e0a56dde95cc6ec952577f67e0dc151eadd928e8d9f6a7f821d69a4a734", size = 14699067 }, + { url = "https://files.pythonhosted.org/packages/aa/f7/799ddd538b21017cbf80294c92e9efbf6db08dff6efee37c3be114a81845/torchvision-0.21.0-cp310-cp310-win_amd64.whl", hash = "sha256:abbf1d7b9d52c00d2af4afa8dac1fb3e2356f662a4566bd98dfaaa3634f4eb34", size = 1560542 }, + { url = "https://files.pythonhosted.org/packages/29/88/00c69db213ee2443ada8886ec60789b227e06bb869d85ee324578221a7f7/torchvision-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110d115333524d60e9e474d53c7d20f096dbd8a080232f88dddb90566f90064c", size = 1784141 }, + { url = "https://files.pythonhosted.org/packages/be/a2/b0cedf0a411f1a5d75cfc0b87cde56dd1ddc1878be46a42c905cd8580220/torchvision-0.21.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:3891cd086c5071bda6b4ee9d266bb2ac39c998c045c2ebcd1e818b8316fb5d41", size = 7237719 }, + { url = "https://files.pythonhosted.org/packages/8c/a1/ee962ef9d0b2bf7a6f8b14cb95acb70e05cd2101af521032a09e43f8582f/torchvision-0.21.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:54454923a50104c66a9ab6bd8b73a11c2fc218c964b1006d5d1fe5b442c3dcb6", size = 14700617 }, + { url = "https://files.pythonhosted.org/packages/88/53/4ad334b9b1d8dd99836869fec139cb74a27781298360b91b9506c53f1d10/torchvision-0.21.0-cp311-cp311-win_amd64.whl", hash = "sha256:49bcfad8cfe2c27dee116c45d4f866d7974bcf14a5a9fbef893635deae322f2f", size = 1560523 }, + { url = "https://files.pythonhosted.org/packages/6e/1b/28f527b22d5e8800184d0bc847f801ae92c7573a8c15979d92b7091c0751/torchvision-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:97a5814a93c793aaf0179cfc7f916024f4b63218929aee977b645633d074a49f", size = 1784140 }, + { url = "https://files.pythonhosted.org/packages/36/63/0722e153fd27d64d5b0af45b5c8cb0e80b35a68cf0130303bc9a8bb095c7/torchvision-0.21.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:b578bcad8a4083b40d34f689b19ca9f7c63e511758d806510ea03c29ac568f7b", size = 7238673 }, + { url = "https://files.pythonhosted.org/packages/bb/ea/03541ed901cdc30b934f897060d09bbf7a98466a08ad1680320f9ce0cbe0/torchvision-0.21.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5083a5b1fec2351bf5ea9900a741d54086db75baec4b1d21e39451e00977f1b1", size = 14701186 }, + { url = "https://files.pythonhosted.org/packages/4c/6a/c7752603060d076dfed95135b78b047dc71792630cbcb022e3693d6f32ef/torchvision-0.21.0-cp312-cp312-win_amd64.whl", hash = "sha256:6eb75d41e3bbfc2f7642d0abba9383cc9ae6c5a4ca8d6b00628c225e1eaa63b3", size = 1560520 }, + { url = "https://files.pythonhosted.org/packages/f9/56/47d456b61c3bbce7bed4af3925c83d405bb87468e659fd3cf3d9840c3b51/torchvision-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:659b76c86757cb2ee4ca2db245e0740cfc3081fef46f0f1064d11adb4a8cee31", size = 1784141 }, + { url = "https://files.pythonhosted.org/packages/cb/4c/99880813aa50e64447fb1c4c6c804a793d2d78f7f7c53e99ddee7fa175fa/torchvision-0.21.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:084ac3f5a1f50c70d630a488d19bf62f323018eae1b1c1232f2b7047d3a7b76d", size = 7238714 }, + { url = "https://files.pythonhosted.org/packages/0b/2d/3c3ee10608310a395594aac7da8640372ed79c6585910ccae6919658dcdc/torchvision-0.21.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5045a3a5f21ec3eea6962fa5f2fa2d4283f854caec25ada493fcf4aab2925467", size = 2281252 }, + { url = "https://files.pythonhosted.org/packages/ed/b4/fc60e3bc003879d3de842baea258fffc3586f4b49cd435a5ba1e09c33315/torchvision-0.21.0-cp313-cp313-win_amd64.whl", hash = "sha256:9147f5e096a9270684e3befdee350f3cacafd48e0c54ab195f45790a9c146d67", size = 1560519 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "transformers" +version = "4.48.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/1093586e09c8d889d2f6b8ffe6a1369e1e179eb7b8e732fc0f348a8fe58f/transformers-4.48.2.tar.gz", hash = "sha256:dcfb73473e61f22fb3366fe2471ed2e42779ecdd49527a1bdf1937574855d516", size = 8370945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/40/902c95a2a6f5d2d120c940ac4bd1f937c01035af529803c13d65ca33c2d1/transformers-4.48.2-py3-none-any.whl", hash = "sha256:493bc5b0268b116eff305edf6656367fc89cf570e7a9d5891369e04751db698a", size = 9667774 }, +] + +[[package]] +name = "triton" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/65/3ffa90e158a2c82f0716eee8d26a725d241549b7d7aaf7e4f44ac03ebd89/triton-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3e54983cd51875855da7c68ec05c05cf8bb08df361b1d5b69e05e40b0c9bd62", size = 253090354 }, + { url = "https://files.pythonhosted.org/packages/a7/2e/757d2280d4fefe7d33af7615124e7e298ae7b8e3bc4446cdb8e88b0f9bab/triton-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8009a1fb093ee8546495e96731336a33fb8856a38e45bb4ab6affd6dbc3ba220", size = 253157636 }, + { url = "https://files.pythonhosted.org/packages/06/00/59500052cb1cf8cf5316be93598946bc451f14072c6ff256904428eaf03c/triton-3.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d9b215efc1c26fa7eefb9a157915c92d52e000d2bf83e5f69704047e63f125c", size = 253159365 }, + { url = "https://files.pythonhosted.org/packages/c7/30/37a3384d1e2e9320331baca41e835e90a3767303642c7a80d4510152cbcf/triton-3.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5dfa23ba84541d7c0a531dfce76d8bcd19159d50a4a8b14ad01e91734a5c1b0", size = 253154278 }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, +] From 050f01388668dce644915da63289de220b63d9d0 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Fri, 4 Jul 2025 16:08:10 +0200 Subject: [PATCH 23/40] Try setup-uv@v6 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d93b3ca..27d0441b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,7 +197,7 @@ jobs: required-ros-distributions: humble - run: 'source /opt/ros/humble/setup.bash && echo AMENT_PREFIX_PATH=${AMENT_PREFIX_PATH} >> "$GITHUB_ENV"' - name: Install the latest version of uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 with: enable-cache: true - name: Install pyarrow From c790b65affd322b13b972505eab0078519bd3a29 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Thu, 3 Jul 2025 17:39:23 +0200 Subject: [PATCH 24/40] convert chinese character to pinyin --- Cargo.lock | 7 +++++++ examples/speech-to-text/whisper-dev.yml | 2 +- node-hub/dora-rerun/Cargo.toml | 1 + node-hub/dora-rerun/src/lib.rs | 20 +++++++++++++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5f3ecd0..7b021dc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3343,6 +3343,7 @@ dependencies = [ "eyre", "k", "ndarray 0.15.6", + "pinyin", "pyo3", "rand 0.9.1", "rerun", @@ -8420,6 +8421,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pinyin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f2611cd06a1ac239a0cea4521de9eb068a6ca110324ee00631aa68daa74fc0" + [[package]] name = "piper" version = "0.2.4" diff --git a/examples/speech-to-text/whisper-dev.yml b/examples/speech-to-text/whisper-dev.yml index 1742bd9f..1c2759b0 100644 --- a/examples/speech-to-text/whisper-dev.yml +++ b/examples/speech-to-text/whisper-dev.yml @@ -28,7 +28,7 @@ nodes: # USE_MODELSCOPE_HUB: true - id: dora-rerun - build: cargo build -p dora-rerun --release + build: pip install -e ../../node-hub/dora-rerun path: dora-rerun inputs: original_text: dora-distil-whisper/text diff --git a/node-hub/dora-rerun/Cargo.toml b/node-hub/dora-rerun/Cargo.toml index d777d312..2070f90d 100644 --- a/node-hub/dora-rerun/Cargo.toml +++ b/node-hub/dora-rerun/Cargo.toml @@ -28,6 +28,7 @@ pyo3 = { workspace = true, features = [ ], optional = true } bytemuck = "1.20.0" rand = "0.9.1" +pinyin = "0.10.0" [lib] diff --git a/node-hub/dora-rerun/src/lib.rs b/node-hub/dora-rerun/src/lib.rs index d0572e51..c9ebb7fb 100644 --- a/node-hub/dora-rerun/src/lib.rs +++ b/node-hub/dora-rerun/src/lib.rs @@ -12,6 +12,7 @@ use dora_node_api::{ }; use eyre::{bail, eyre, Context, Result}; +use pinyin::ToPinyin; use rerun::{ components::ImageBuffer, external::log::warn, ImageFormat, Points2D, Points3D, SpawnOptions, }; @@ -317,7 +318,24 @@ pub fn lib_main() -> Result<()> { let buffer: StringArray = data.to_data().into(); buffer.iter().try_for_each(|string| -> Result<()> { if let Some(str) = string { - rec.log(id.as_str(), &rerun::TextLog::new(str)) + let chars = str.chars().collect::>(); + let mut new_string = vec![]; + for char in chars { + // Check if the character is a Chinese character + if char.is_ascii() || char.is_control() { + new_string.push(char); + continue; + } + // If it is a Chinese character, replace it with its pinyin + if let Some(pinyin) = char.to_pinyin() { + for char in pinyin.with_tone().chars() { + new_string.push(char); + } + new_string.push(' '); + } + } + let pinyined_str = new_string.iter().collect::(); + rec.log(id.as_str(), &rerun::TextLog::new(pinyined_str)) .wrap_err("Could not log text") } else { Ok(()) From a6646da2c029f60fb4b55033dd45ec3f3403ea92 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Wed, 2 Jul 2025 10:49:28 +0200 Subject: [PATCH 25/40] Add exif to avif file with focal information --- Cargo.lock | 42 ++++++++++++++++++---- node-hub/dora-rav1e/Cargo.toml | 3 +- node-hub/dora-rav1e/src/lib.rs | 66 +++++++++++++++++++++++++++------- 3 files changed, 91 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5f3ecd0..45750dcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1166,8 +1166,7 @@ dependencies = [ [[package]] name = "avif-serialize" version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19135c0c7a60bfee564dbe44ab5ce0557c6bf3884e5291a50be76a15640c4fbd" +source = "git+https://github.com/haixuantao/avif-serialize#667a6bb006f14adc5a217c03eb2d5af49e7ac05c" dependencies = [ "arrayvec", ] @@ -2279,6 +2278,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -3317,6 +3331,7 @@ dependencies = [ "bytemuck", "dora-node-api", "eyre", + "little_exif", "log", "pyo3", "rav1e", @@ -6378,6 +6393,19 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +[[package]] +name = "little_exif" +version = "0.6.9-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1ec10e5465734b7d52c0acf732cf606f3129af347dff310d27ce4ccfe7d07f" +dependencies = [ + "crc", + "log", + "miniz_oxide", + "paste", + "quick-xml 0.37.5", +] + [[package]] name = "llguidance" version = "0.7.0" @@ -6738,9 +6766,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -9028,9 +9056,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -14604,7 +14632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", - "quick-xml 0.37.2", + "quick-xml 0.37.5", "quote", ] diff --git a/node-hub/dora-rav1e/Cargo.toml b/node-hub/dora-rav1e/Cargo.toml index e69302e1..009b8b28 100644 --- a/node-hub/dora-rav1e/Cargo.toml +++ b/node-hub/dora-rav1e/Cargo.toml @@ -25,7 +25,8 @@ pyo3 = { workspace = true, features = [ "eyre", "generate-import-lib", ], optional = true } -avif-serialize = "0.8.4" +avif-serialize = { git = "https://github.com/haixuantao/avif-serialize" } +little_exif = { version = "0.6.9-beta.1" } [lib] diff --git a/node-hub/dora-rav1e/src/lib.rs b/node-hub/dora-rav1e/src/lib.rs index 68280155..928613d7 100644 --- a/node-hub/dora-rav1e/src/lib.rs +++ b/node-hub/dora-rav1e/src/lib.rs @@ -8,12 +8,16 @@ // PATENTS file, you can obtain it at www.aomedia.org/license/patent. use std::env::var; +use std::vec; use dora_node_api::arrow::array::AsArray; use dora_node_api::arrow::datatypes::{UInt16Type, UInt8Type}; use dora_node_api::dora_core::config::DataId; -use dora_node_api::{DoraNode, Event, IntoArrow, Metadata, Parameter}; +use dora_node_api::{DoraNode, Event, IntoArrow, Metadata, MetadataParameters, Parameter}; use eyre::{Context as EyreContext, Result}; +use little_exif::exif_tag::ExifTag; +use little_exif::metadata::Metadata as ExifMetadata; +use little_exif::rational::uR64; use log::warn; use rav1e::color::{ColorDescription, MatrixCoefficients}; // Encode the same tiny blank frame 30 times @@ -56,6 +60,25 @@ pub fn fill_zeros_toward_center_y_plane_in_place(y: &mut [u16], width: usize, he } } +fn metadata_to_exif(metadata: &MetadataParameters) -> Result> { + let mut metadata_exif = ExifMetadata::new(); + metadata_exif.set_tag(ExifTag::Software("dora-rs".to_string())); + if let Some(Parameter::ListInt(focal_lengths)) = metadata.get("focal") { + metadata_exif.set_tag(ExifTag::FocalLength( + focal_lengths + .iter() + .map(|&f| uR64 { + nominator: f as u32, + denominator: 1, + }) + .collect::>(), + )); + } + + let vector = metadata_exif.as_u8_vec(little_exif::filetype::FileExtension::HEIF)?; + return Ok(vector); +} + fn bgr8_to_yuv420(bgr_data: Vec, width: usize, height: usize) -> (Vec, Vec, Vec) { let mut y_plane = vec![0; width * height]; let mut u_plane = vec![0; (width / 2) * (height / 2)]; @@ -159,9 +182,18 @@ fn send_yuv( } else { MatrixCoefficients::BT709 }; - let data = avif_serialize::Aviffy::new() + let mut aviffy = avif_serialize::Aviffy::new(); + aviffy .set_chroma_subsampling((true, true)) - .set_seq_profile(0) + .set_seq_profile(0); + + let aviffy = if let Ok(exif) = metadata_to_exif(&metadata.parameters) { + aviffy.set_exif(exif) + } else { + &mut aviffy + }; + + let data = aviffy .matrix_coefficients(match matrix_coefficients { MatrixCoefficients::Identity => { avif_serialize::constants::MatrixCoefficients::Rgb @@ -375,17 +407,27 @@ pub fn lib_main() -> Result<()> { Parameter::String("avif".to_string()), ); - let data = avif_serialize::Aviffy::new() + let mut aviffy = avif_serialize::Aviffy::new(); + aviffy .full_color_range(false) .set_seq_profile(0) - .set_monochrome(true) - .to_vec( - &data, - None, - enc.width as u32, - enc.height as u32, - enc.bit_depth as u8, - ); + .set_monochrome(true); + + let aviffy = if let Ok(exif) = + metadata_to_exif(&metadata.parameters) + { + aviffy.set_exif(exif) + } else { + &mut aviffy + }; + + let data = aviffy.to_vec( + &data, + None, + enc.width as u32, + enc.height as u32, + enc.bit_depth as u8, + ); let arrow = data.into_arrow(); From ed014b97ff3a32c5928ff084dbdabd422cf59188 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Wed, 2 Jul 2025 10:49:38 +0200 Subject: [PATCH 26/40] Add examples --- examples/vggt/depth-to-avif.yaml | 2 +- examples/vggt/realsense-to-avif.yaml | 53 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 examples/vggt/realsense-to-avif.yaml diff --git a/examples/vggt/depth-to-avif.yaml b/examples/vggt/depth-to-avif.yaml index 6db92ac3..2ec3af0b 100644 --- a/examples/vggt/depth-to-avif.yaml +++ b/examples/vggt/depth-to-avif.yaml @@ -43,7 +43,7 @@ nodes: - id: bench path: image_saver.py inputs: - camera_depth: rav1e-image/image + vggt_image: rav1e-image/image vggt_depth: rav1e-depth/depth - id: plot diff --git a/examples/vggt/realsense-to-avif.yaml b/examples/vggt/realsense-to-avif.yaml new file mode 100644 index 00000000..408b8323 --- /dev/null +++ b/examples/vggt/realsense-to-avif.yaml @@ -0,0 +1,53 @@ +nodes: + - id: camera + build: pip install -e ../../node-hub/dora-pyrealsense + path: dora-pyrealsense + inputs: + tick: dora/timer/millis/100 + outputs: + - image + - depth + + - id: dora-vggt + build: pip install -e ../../node-hub/dora-vggt + path: dora-vggt + inputs: + image: camera/image + outputs: + - depth + - image + env: + DEPTH_ENCODING: mono16 + + - id: rav1e-depth-vggt + path: dora-rav1e + build: cargo build -p dora-rav1e --release + inputs: + depth: dora-vggt/depth + outputs: + - depth + env: + ENCODING: avif + + - id: rav1e-depth-realsense + path: dora-rav1e + build: cargo build -p dora-rav1e --release + inputs: + depth: camera/depth + outputs: + - depth + env: + ENCODING: avif + + - id: bench + path: image_saver.py + inputs: + camera_depth: rav1e-depth-vggt/depth + vggt_depth: rav1e-depth-realsense/depth + + - id: plot + build: pip install dora-rerun + path: dora-rerun + inputs: + camera/image: dora-vggt/image + camera/depth: dora-vggt/depth From 549c7195bf640ab038ef96defe6bf5979f196093 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Wed, 2 Jul 2025 10:49:50 +0200 Subject: [PATCH 27/40] Add focal data for vggt --- Cargo.lock | 2 +- node-hub/dora-vggt/dora_vggt/main.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 45750dcf..3a4743f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12027,7 +12027,7 @@ dependencies = [ "hyper 1.6.0", "indicatif", "log", - "quick-xml 0.37.2", + "quick-xml 0.37.5", "regex", "reqwest", "self-replace", diff --git a/node-hub/dora-vggt/dora_vggt/main.py b/node-hub/dora-vggt/dora_vggt/main.py index 000898e0..e494c8e3 100644 --- a/node-hub/dora-vggt/dora_vggt/main.py +++ b/node-hub/dora-vggt/dora_vggt/main.py @@ -146,6 +146,14 @@ def main(): "encoding": "rgb8", "width": image.shape[1], "height": image.shape[0], + "focal": [ + int(f_0), + int(f_1), + ], + "resolution": [ + int(r_0), + int(r_1), + ], }, ) From f4ddda07c8373634c2719f58dda7e84f5ffad80f Mon Sep 17 00:00:00 2001 From: haixuantao Date: Fri, 4 Jul 2025 12:22:21 +0200 Subject: [PATCH 28/40] Bump to cargo version --- Cargo.lock | 9 +++++---- examples/vggt/depth-to-avif.yaml | 4 ++-- node-hub/dora-rav1e/Cargo.toml | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a4743f9..6ff733ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1165,8 +1165,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.4" -source = "git+https://github.com/haixuantao/avif-serialize#667a6bb006f14adc5a217c03eb2d5af49e7ac05c" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" dependencies = [ "arrayvec", ] @@ -6395,9 +6396,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "little_exif" -version = "0.6.9-beta.1" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1ec10e5465734b7d52c0acf732cf606f3129af347dff310d27ce4ccfe7d07f" +checksum = "9ebaa2b90127fb33dcf2ec1f5be8b496e92d5d1736789decca8eb2798c52f2e7" dependencies = [ "crc", "log", diff --git a/examples/vggt/depth-to-avif.yaml b/examples/vggt/depth-to-avif.yaml index 2ec3af0b..a7f34fc4 100644 --- a/examples/vggt/depth-to-avif.yaml +++ b/examples/vggt/depth-to-avif.yaml @@ -22,7 +22,7 @@ nodes: - id: rav1e-depth path: dora-rav1e - build: cargo build -p dora-rav1e --release + build: pip install -e ../../node-hub/dora-rav1e inputs: depth: dora-vggt/depth outputs: @@ -32,7 +32,7 @@ nodes: - id: rav1e-image path: dora-rav1e - build: cargo build -p dora-rav1e --release + build: pip install -e ../../node-hub/dora-rav1e inputs: image: dora-vggt/image outputs: diff --git a/node-hub/dora-rav1e/Cargo.toml b/node-hub/dora-rav1e/Cargo.toml index 009b8b28..b71cf93e 100644 --- a/node-hub/dora-rav1e/Cargo.toml +++ b/node-hub/dora-rav1e/Cargo.toml @@ -25,8 +25,8 @@ pyo3 = { workspace = true, features = [ "eyre", "generate-import-lib", ], optional = true } -avif-serialize = { git = "https://github.com/haixuantao/avif-serialize" } -little_exif = { version = "0.6.9-beta.1" } +avif-serialize = { version = "0.8.5" } +little_exif = { version = "0.6.9" } [lib] From b26f8aefbe709114fbace3627a59edc553435951 Mon Sep 17 00:00:00 2001 From: Chrislearn Young Date: Sun, 6 Jul 2025 21:54:26 +0800 Subject: [PATCH 29/40] docs: fix speech-to-speech example README --- examples/speech-to-speech/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/speech-to-speech/README.md b/examples/speech-to-speech/README.md index 9a1ab766..ba05dbdf 100644 --- a/examples/speech-to-speech/README.md +++ b/examples/speech-to-speech/README.md @@ -1,4 +1,4 @@ -# Dora Speech to Text example +# Dora Speech to Speech example Make sure to have, dora, pip and cargo installed. @@ -23,6 +23,6 @@ sudo apt-get install espeak ```bash uv venv --seed -p 3.11 uv pip install -e ../../apis/python/node --reinstall -dora build kokoro-dev.yml -dora run kokoro-dev.yml +dora build kokoro-dev.yml --uv +dora run kokoro-dev.yml --uv ``` From cc9eeac472b723534fca69878f4c7f623abb9ba3 Mon Sep 17 00:00:00 2001 From: Chrislearn Young Date: Mon, 7 Jul 2025 18:03:32 +0800 Subject: [PATCH 30/40] Made some changes to make clippy happy --- apis/rust/node/src/node/arrow_utils.rs | 4 ++-- apis/rust/node/src/node/mod.rs | 1 + libraries/core/src/descriptor/validate.rs | 2 +- libraries/extensions/telemetry/tracing/src/telemetry.rs | 2 +- node-hub/openai-proxy-server/src/main.rs | 5 +++-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apis/rust/node/src/node/arrow_utils.rs b/apis/rust/node/src/node/arrow_utils.rs index 247c76ae..5a21337a 100644 --- a/apis/rust/node/src/node/arrow_utils.rs +++ b/apis/rust/node/src/node/arrow_utils.rs @@ -11,7 +11,7 @@ fn required_data_size_inner(array: &ArrayData, next_offset: &mut usize) { for (buffer, spec) in array.buffers().iter().zip(&layout.buffers) { // consider alignment padding if let BufferSpec::FixedWidth { alignment, .. } = spec { - *next_offset = (*next_offset + alignment - 1) / alignment * alignment; + *next_offset = (*next_offset).div_ceil(*alignment) * alignment; } *next_offset += buffer.len(); } @@ -42,7 +42,7 @@ fn copy_array_into_sample_inner( ); // add alignment padding if let BufferSpec::FixedWidth { alignment, .. } = spec { - *next_offset = (*next_offset + alignment - 1) / alignment * alignment; + *next_offset = (*next_offset).div_ceil(*alignment) * alignment; } target_buffer[*next_offset..][..len].copy_from_slice(buffer.as_slice()); diff --git a/apis/rust/node/src/node/mod.rs b/apis/rust/node/src/node/mod.rs index 7b4a109c..0c986ad5 100644 --- a/apis/rust/node/src/node/mod.rs +++ b/apis/rust/node/src/node/mod.rs @@ -44,6 +44,7 @@ mod drop_stream; pub const ZERO_COPY_THRESHOLD: usize = 4096; +#[allow(dead_code)] enum TokioRuntime { Runtime(Runtime), Handle(Handle), diff --git a/libraries/core/src/descriptor/validate.rs b/libraries/core/src/descriptor/validate.rs index f68979cd..da4a3798 100644 --- a/libraries/core/src/descriptor/validate.rs +++ b/libraries/core/src/descriptor/validate.rs @@ -54,7 +54,7 @@ pub fn check_dataflow( }; } }, - dora_message::descriptor::NodeSource::GitBranch { repo, rev } => { + dora_message::descriptor::NodeSource::GitBranch { .. } => { info!("skipping check for node with git source"); } }, diff --git a/libraries/extensions/telemetry/tracing/src/telemetry.rs b/libraries/extensions/telemetry/tracing/src/telemetry.rs index 526fe970..1d4e6772 100644 --- a/libraries/extensions/telemetry/tracing/src/telemetry.rs +++ b/libraries/extensions/telemetry/tracing/src/telemetry.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; struct MetadataMap<'a>(HashMap<&'a str, &'a str>); -impl<'a> Extractor for MetadataMap<'a> { +impl Extractor for MetadataMap<'_> { /// Get a value for a key from the MetadataMap. If the value can't be converted to &str, returns None fn get(&self, key: &str) -> Option<&str> { self.0.get(key).cloned() diff --git a/node-hub/openai-proxy-server/src/main.rs b/node-hub/openai-proxy-server/src/main.rs index 50e73880..4cee442f 100644 --- a/node-hub/openai-proxy-server/src/main.rs +++ b/node-hub/openai-proxy-server/src/main.rs @@ -96,7 +96,7 @@ async fn main() -> eyre::Result<()> { ) .context("failed to send dora output")?; - reply_channels.push_back((reply, 0 as u64, request.model)); + reply_channels.push_back((reply, 0_u64, request.model)); } }, dora_node_api::merged::MergedEvent::Dora(event) => match event { @@ -112,7 +112,7 @@ async fn main() -> eyre::Result<()> { let data = data.as_string::(); let string = data.iter().fold("".to_string(), |mut acc, s| { if let Some(s) = s { - acc.push_str("\n"); + acc.push('\n'); acc.push_str(s); } acc @@ -164,6 +164,7 @@ async fn main() -> eyre::Result<()> { Ok(()) } +#[allow(clippy::large_enum_variant)] enum ServerEvent { Result(eyre::Result<()>), ChatCompletionRequest { From 3bea920cfcffaefc0ee3a9cc252223fb05ac167b Mon Sep 17 00:00:00 2001 From: Chrislearn Young Date: Mon, 7 Jul 2025 19:04:59 +0800 Subject: [PATCH 31/40] cargo clippy for node-hub --- node-hub/dora-rav1e/src/lib.rs | 26 ++++++++++++-------------- node-hub/dora-rerun/src/lib.rs | 14 ++++++-------- node-hub/dora-rerun/src/series.rs | 2 +- node-hub/dora-rerun/src/urdf.rs | 2 +- node-hub/dora-rustypot/src/lib.rs | 16 ++++++++-------- 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/node-hub/dora-rav1e/src/lib.rs b/node-hub/dora-rav1e/src/lib.rs index 68280155..dfa06813 100644 --- a/node-hub/dora-rav1e/src/lib.rs +++ b/node-hub/dora-rav1e/src/lib.rs @@ -107,6 +107,7 @@ fn get_yuv_planes(buffer: &[u8], width: usize, height: usize) -> (&[u8], &[u8], (y_plane, u_plane, v_plane) } +#[allow(clippy::too_many_arguments)] fn send_yuv( y: &[u8], u: &[u8], @@ -118,7 +119,7 @@ fn send_yuv( id: DataId, metadata: &mut Metadata, output_encoding: &str, -) -> () { +) { // Create a new Arrow array for the YUV420 data let cfg = Config::new().with_encoder_config(enc.clone()); let mut ctx: Context = cfg.new_context().unwrap(); @@ -126,13 +127,13 @@ fn send_yuv( let xdec = f.planes[0].cfg.xdec; let stride = (width + xdec) >> xdec; - f.planes[0].copy_from_raw_u8(&y, stride, 1); + f.planes[0].copy_from_raw_u8(y, stride, 1); let xdec = f.planes[1].cfg.xdec; let stride = (width + xdec) >> xdec; - f.planes[1].copy_from_raw_u8(&u, stride, 1); + f.planes[1].copy_from_raw_u8(u, stride, 1); let xdec = f.planes[2].cfg.xdec; let stride = (width + xdec) >> xdec; - f.planes[2].copy_from_raw_u8(&v, stride, 1); + f.planes[2].copy_from_raw_u8(v, stride, 1); match ctx.send_frame(f) { Ok(_) => {} @@ -289,12 +290,9 @@ pub fn lib_main() -> Result<()> { chroma_sampling: color::ChromaSampling::Cs420, ..Default::default() }; - match encoding { - "mono16" => { - enc.bit_depth = 12; - enc.chroma_sampling = color::ChromaSampling::Cs400; - } - _ => {} + if encoding == "mono16" { + enc.bit_depth = 12; + enc.chroma_sampling = color::ChromaSampling::Cs400; } if encoding == "bgr8" { @@ -320,9 +318,9 @@ pub fn lib_main() -> Result<()> { let (y, u, v) = get_yuv_planes(buffer, width, height); send_yuv( - &y, - &u, - &v, + y, + u, + v, enc, width, height, @@ -342,7 +340,7 @@ pub fn lib_main() -> Result<()> { fill_zeros_toward_center_y_plane_in_place(&mut buffer, width, height); } - let bytes: &[u8] = &bytemuck::cast_slice(&buffer); + let bytes: &[u8] = bytemuck::cast_slice(&buffer); let cfg = Config::new().with_encoder_config(enc.clone()); let mut ctx: Context = cfg.new_context().unwrap(); diff --git a/node-hub/dora-rerun/src/lib.rs b/node-hub/dora-rerun/src/lib.rs index d0572e51..66d731f2 100644 --- a/node-hub/dora-rerun/src/lib.rs +++ b/node-hub/dora-rerun/src/lib.rs @@ -160,8 +160,7 @@ pub fn lib_main() -> Result<()> { let buffer: Vec = buffer.chunks(3).flat_map(|x| [x[2], x[1], x[0]]).collect(); image_cache.insert(id.clone(), buffer.clone()); - let image_buffer = ImageBuffer::try_from(buffer) - .context("Could not convert buffer to image buffer")?; + let image_buffer = ImageBuffer::from(buffer); // let tensordata = ImageBuffer(buffer); let image = rerun::Image::new( @@ -174,8 +173,7 @@ pub fn lib_main() -> Result<()> { let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); image_cache.insert(id.clone(), buffer.values().to_vec()); let buffer: &[u8] = buffer.values(); - let image_buffer = ImageBuffer::try_from(buffer) - .context("Could not convert buffer to image buffer")?; + let image_buffer = ImageBuffer::from(buffer); let image = rerun::Image::new( image_buffer, @@ -385,12 +383,12 @@ pub fn lib_main() -> Result<()> { // Get color or assign random color in cache let color = color_cache.get(&id); let color = if let Some(color) = color { - color.clone() + *color } else { let color = rerun::Color::from_rgb(rand::random::(), 180, rand::random::()); - color_cache.insert(id.clone(), color.clone()); + color_cache.insert(id.clone(), color); color }; let dataid = id; @@ -412,12 +410,12 @@ pub fn lib_main() -> Result<()> { // Get color or assign random color in cache let color = color_cache.get(&id); let color = if let Some(color) = color { - color.clone() + *color } else { let color = rerun::Color::from_rgb(rand::random::(), 180, rand::random::()); - color_cache.insert(id.clone(), color.clone()); + color_cache.insert(id.clone(), color); color }; let dataid = id; diff --git a/node-hub/dora-rerun/src/series.rs b/node-hub/dora-rerun/src/series.rs index 931db8af..ea81d003 100644 --- a/node-hub/dora-rerun/src/series.rs +++ b/node-hub/dora-rerun/src/series.rs @@ -7,7 +7,7 @@ pub fn update_series(rec: &RecordingStream, id: DataId, data: ArrowData) -> Resu for (i, value) in series.iter().enumerate() { rec.log( format!("{}_{}", id.as_str(), i), - &rerun::Scalar::new(*value as f64), + &rerun::Scalars::new([*value]), ) .wrap_err("could not log series")?; } diff --git a/node-hub/dora-rerun/src/urdf.rs b/node-hub/dora-rerun/src/urdf.rs index 4241df3d..ffe763d9 100644 --- a/node-hub/dora-rerun/src/urdf.rs +++ b/node-hub/dora-rerun/src/urdf.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt::format, path::PathBuf}; +use std::{collections::HashMap, path::PathBuf}; use eyre::{Context, ContextCompat, Result}; use k::{nalgebra::Quaternion, Chain, Translation3, UnitQuaternion}; diff --git a/node-hub/dora-rustypot/src/lib.rs b/node-hub/dora-rustypot/src/lib.rs index ed98bde8..85f21152 100644 --- a/node-hub/dora-rustypot/src/lib.rs +++ b/node-hub/dora-rustypot/src/lib.rs @@ -44,12 +44,13 @@ pub fn lib_main() -> Result<()> { } while let Some(event) = events.recv() { - match event { - Event::Input { - id, - metadata: _, - data, - } => match id.as_str() { + if let Event::Input { + id, + metadata: _, + data, + } = event + { + match id.as_str() { "tick" => { if let Ok(joints) = c.read_present_position(&ids) { let mut parameter = BTreeMap::new(); @@ -70,8 +71,7 @@ pub fn lib_main() -> Result<()> { c.write_goal_position(&ids, &data).unwrap(); } other => eprintln!("Received input `{other}`"), - }, - _ => {} + } } } From bdc2356348caa95057d8c514549f55709c0aac39 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Thu, 3 Jul 2025 17:27:10 +0200 Subject: [PATCH 32/40] Removing support for macos x86 in order to unpin macos version --- .github/workflows/node_hub_test.sh | 8 -------- .github/workflows/pip-release.yml | 2 -- README.md | 26 +++++++++++++------------- apis/python/node/pyproject.toml | 8 -------- binaries/cli/pyproject.toml | 8 -------- 5 files changed, 13 insertions(+), 39 deletions(-) diff --git a/.github/workflows/node_hub_test.sh b/.github/workflows/node_hub_test.sh index d7e6e4b5..5fa231cf 100755 --- a/.github/workflows/node_hub_test.sh +++ b/.github/workflows/node_hub_test.sh @@ -89,14 +89,6 @@ else if [ "$GITHUB_EVENT_NAME" == "release" ] || [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then maturin publish --skip-existing fi - - # x86_64-apple-darwin - rustup target add x86_64-apple-darwin - maturin build --target x86_64-apple-darwin --zig --release - # If GITHUB_EVENT_NAME is release or workflow_dispatch, publish the wheel - if [ "$GITHUB_EVENT_NAME" == "release" ] || [ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]; then - maturin publish --target x86_64-apple-darwin --skip-existing --zig - fi elif [[ "$(uname)" = "Linux" ]] || [[ "$CI" == "false" ]]; then if [ -f "$dir/Cargo.toml" ]; then diff --git a/.github/workflows/pip-release.yml b/.github/workflows/pip-release.yml index 711698fb..742defbc 100644 --- a/.github/workflows/pip-release.yml +++ b/.github/workflows/pip-release.yml @@ -192,8 +192,6 @@ jobs: fail-fast: false matrix: platform: - - runner: macos-13 - target: x86_64 - runner: macos-13 target: aarch64 repository: diff --git a/README.md b/README.md index a4dde388..6bd9ce1b 100644 --- a/README.md +++ b/README.md @@ -76,16 +76,16 @@ ## Support Matrix -| | dora-rs | -| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **APIs** | Python >= 3.7 including sync β­βœ…
Rust βœ…
C/C++ πŸ†—
ROS2 >= Foxy πŸ†— | -| **OS** | Linux: Arm 32 β­βœ… Arm 64 β­βœ… x64_86 β­βœ…
MacOS: Arm 64 β­βœ… x64_86 βœ…
Windows: x64_86 πŸ†—
WSL: x64_86 πŸ†—
Android: πŸ› οΈ (Blocked by: https://github.com/elast0ny/shared_memory/issues/32)
IOS: πŸ› οΈ | -| **Message Format** | Arrow βœ…
Standard Specification πŸ› οΈ | -| **Local Communication** | Shared Memory βœ…
[Cuda IPC](https://arrow.apache.org/docs/python/api/cuda.html) πŸ“ | -| **Remote Communication** | [Zenoh](https://zenoh.io/) πŸ“ | -| **Metrics, Tracing, and Logging** | Opentelemetry πŸ“ | -| **Configuration** | YAML βœ… | -| **Package Manager** | [pip](https://pypi.org/): Python Node βœ… Rust Node βœ… C/C++ Node πŸ› οΈ
[cargo](https://crates.io/): Rust Node βœ… | +| | dora-rs | +| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **APIs** | Python >= 3.7 including sync β­βœ…
Rust βœ…
C/C++ πŸ†—
ROS2 >= Foxy πŸ†— | +| **OS** | Linux: Arm 32 β­βœ… Arm 64 β­βœ… x64_86 β­βœ…
MacOS: Arm 64 β­βœ…
Windows: x64_86 πŸ†—
WSL: x64_86 πŸ†—
Android: πŸ› οΈ (Blocked by: https://github.com/elast0ny/shared_memory/issues/32)
IOS: πŸ› οΈ | +| **Message Format** | Arrow βœ…
Standard Specification πŸ› οΈ | +| **Local Communication** | Shared Memory βœ…
[Cuda IPC](https://arrow.apache.org/docs/python/api/cuda.html) πŸ“ | +| **Remote Communication** | [Zenoh](https://zenoh.io/) πŸ“ | +| **Metrics, Tracing, and Logging** | Opentelemetry πŸ“ | +| **Configuration** | YAML βœ… | +| **Package Manager** | [pip](https://pypi.org/): Python Node βœ… Rust Node βœ… C/C++ Node πŸ› οΈ
[cargo](https://crates.io/): Rust Node βœ… | > - ⭐ = Recommended > - βœ… = First Class Support @@ -104,8 +104,8 @@ | Camera | [PyOrbbeckSDK](https://github.com/dora-rs/dora/blob/main/node-hub/dora-pyorbbecksdk) | πŸ“ | Image and depth from Orbbeck Camera | ![Downloads](https://img.shields.io/pypi/dm/dora-pyorbbecksdk?label=%20) | ![License](https://img.shields.io/pypi/l/dora-pyorbbecksdk?label=%20) | | Camera | [PyRealsense](https://github.com/dora-rs/dora/blob/main/node-hub/dora-pyrealsense) | LinuxπŸ†—
MacπŸ› οΈ | Image and depth from Realsense | ![Downloads](https://img.shields.io/pypi/dm/dora-pyrealsense?label=%20) | ![License](https://img.shields.io/pypi/l/dora-pyrealsense?label=%20) | | Camera | [OpenCV Video Capture](https://github.com/dora-rs/dora/blob/main/node-hub/opencv-video-capture) | βœ… | Image stream from OpenCV Camera | ![Downloads](https://img.shields.io/pypi/dm/opencv-video-capture?label=%20) | ![License](https://img.shields.io/pypi/l/opencv-video-capture?label=%20) | -| Camera | [Kornia V4L Capture](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-v4l-capture) | βœ… | Video stream for Linux Camera (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | -| Camera | [Kornia GST Capture](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-gst-capture) | βœ… | Video Capture using Gstreamer (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | +| Camera | [Kornia V4L Capture](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-v4l-capture) | βœ… | Video stream for Linux Camera (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | +| Camera | [Kornia GST Capture](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-gst-capture) | βœ… | Video Capture using Gstreamer (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | | Peripheral | [Keyboard](https://github.com/dora-rs/dora/blob/main/node-hub/dora-keyboard) | βœ… | Keyboard char listener | ![Downloads](https://img.shields.io/pypi/dm/dora-keyboard?label=%20) | ![License](https://img.shields.io/pypi/l/dora-keyboard?label=%20) | | Peripheral | [Microphone](https://github.com/dora-rs/dora/blob/main/node-hub/dora-microphone) | βœ… | Audio from microphone | ![Downloads](https://img.shields.io/pypi/dm/dora-microphone?label=%20) | ![License](https://img.shields.io/pypi/l/dora-microphone?label=%20) | | Peripheral | [PyAudio(Speaker)](https://github.com/dora-rs/dora/blob/main/node-hub/dora-pyaudio) | βœ… | Output audio from speaker | ![Downloads](https://img.shields.io/pypi/dm/dora-pyaudio?label=%20) | ![License](https://img.shields.io/pypi/l/dora-pyaudio?label=%20) | @@ -138,7 +138,7 @@ | Simulator | [Mujoco](https://github.com/dora-rs/dora-lerobot/blob/main/node-hub/mujoco-client) | πŸ“ | Mujoco Simulator | | | | Simulator | [Carla](https://github.com/dora-rs/dora-drives) | πŸ“ | Carla Simulator | | | | Simulator | [Gymnasium](https://github.com/dora-rs/dora-lerobot/blob/main/gym_dora) | πŸ“ | Experimental OpenAI Gymnasium bridge | | | -| Image Processing | [Kornia Sobel Operator](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-imgproc-sobel) | βœ… | Kornia image processing Sobel operator (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | +| Image Processing | [Kornia Sobel Operator](https://github.com/kornia/dora-nodes-hub/tree/main/kornia-imgproc-sobel) | βœ… | Kornia image processing Sobel operator (rust) | | ![License](https://img.shields.io/badge/license-Apache%202-blue) | ## Examples diff --git a/apis/python/node/pyproject.toml b/apis/python/node/pyproject.toml index 8636df49..33048a3f 100644 --- a/apis/python/node/pyproject.toml +++ b/apis/python/node/pyproject.toml @@ -22,11 +22,3 @@ extend-select = [ "D", # pydocstyle "UP", ] - -[tool.maturin.target.x86_64-apple-darwin] -# macOS deployment target SDK version -macos-deployment-target = "14.5" - -[tool.maturin.target.aarch64-apple-darwin] -# macOS deployment target SDK version -macos-deployment-target = "14.5" diff --git a/binaries/cli/pyproject.toml b/binaries/cli/pyproject.toml index c2d52457..e797a0be 100644 --- a/binaries/cli/pyproject.toml +++ b/binaries/cli/pyproject.toml @@ -18,11 +18,3 @@ extend-select = [ "D", # pydocstyle "UP", ] - -[tool.maturin.target.x86_64-apple-darwin] -# macOS deployment target SDK version -macos-deployment-target = "14.5" - -[tool.maturin.target.aarch64-apple-darwin] -# macOS deployment target SDK version -macos-deployment-target = "14.5" From 6119b36edb3e26b0286d9330ba8b83e396154987 Mon Sep 17 00:00:00 2001 From: sjfhsjfh Date: Tue, 8 Jul 2025 19:28:42 +0800 Subject: [PATCH 33/40] style: whitespace --- binaries/cli/src/command/build/distributed.rs | 2 +- binaries/cli/src/command/build/local.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/binaries/cli/src/command/build/distributed.rs b/binaries/cli/src/command/build/distributed.rs index 1f6591fe..1fd1ed91 100644 --- a/binaries/cli/src/command/build/distributed.rs +++ b/binaries/cli/src/command/build/distributed.rs @@ -104,4 +104,4 @@ pub fn wait_until_dataflow_built( ControlRequestReply::Error(err) => bail!("{err}"), other => bail!("unexpected start dataflow reply: {other:?}"), } -} \ No newline at end of file +} diff --git a/binaries/cli/src/command/build/local.rs b/binaries/cli/src/command/build/local.rs index 6fed9747..7f6c2557 100644 --- a/binaries/cli/src/command/build/local.rs +++ b/binaries/cli/src/command/build/local.rs @@ -118,4 +118,4 @@ impl BuildLogger for LocalBuildLogger { node_id: self.node_id.clone(), }) } -} \ No newline at end of file +} From 20c9269f6415d6858a16a5f686909f182b77eadc Mon Sep 17 00:00:00 2001 From: sjfhsjfh Date: Wed, 9 Jul 2025 00:05:13 +0800 Subject: [PATCH 34/40] chore: make run function public --- apis/python/node/src/lib.rs | 2 +- binaries/cli/src/command/mod.rs | 2 + binaries/cli/src/command/run.rs | 69 +++++++++++++++++---------------- binaries/cli/src/lib.rs | 2 + 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/apis/python/node/src/lib.rs b/apis/python/node/src/lib.rs index 18e70c3e..6b279ca1 100644 --- a/apis/python/node/src/lib.rs +++ b/apis/python/node/src/lib.rs @@ -381,7 +381,7 @@ pub fn resolve_dataflow(dataflow: String) -> eyre::Result { #[pyfunction] #[pyo3(signature = (dataflow_path, uv=None))] pub fn run(dataflow_path: String, uv: Option) -> eyre::Result<()> { - dora_cli::command::run(dataflow_path, uv.unwrap_or_default()) + dora_cli::run_func(dataflow_path, uv.unwrap_or_default()) } #[pymodule] diff --git a/binaries/cli/src/command/mod.rs b/binaries/cli/src/command/mod.rs index 7ab3804a..617f9b05 100644 --- a/binaries/cli/src/command/mod.rs +++ b/binaries/cli/src/command/mod.rs @@ -14,6 +14,8 @@ mod start; mod stop; mod up; +pub use run::run_func; + use build::Build; use check::Check; use coordinator::Coordinator; diff --git a/binaries/cli/src/command/run.rs b/binaries/cli/src/command/run.rs index 50cfd78a..61b9f0db 100644 --- a/binaries/cli/src/command/run.rs +++ b/binaries/cli/src/command/run.rs @@ -23,41 +23,44 @@ pub struct Run { uv: bool, } -impl Executable for Run { - fn execute(self) -> eyre::Result<()> { - #[cfg(feature = "tracing")] - { - let log_level = std::env::var("RUST_LOG").ok().unwrap_or("info".to_string()); - TracingBuilder::new("run") - .with_stdout(log_level) - .build() - .wrap_err("failed to set up tracing subscriber")?; - } - - let dataflow_path = - resolve_dataflow(self.dataflow).context("could not resolve dataflow")?; - let dataflow_session = DataflowSession::read_session(&dataflow_path) - .context("failed to read DataflowSession")?; - let rt = Builder::new_multi_thread() - .enable_all() +pub fn run_func(dataflow: String, uv: bool) -> eyre::Result<()> { + #[cfg(feature = "tracing")] + { + let log_level = std::env::var("RUST_LOG").ok().unwrap_or("info".to_string()); + TracingBuilder::new("run") + .with_stdout(log_level) .build() - .context("tokio runtime failed")?; + .wrap_err("failed to set up tracing subscriber")?; + } - let (log_tx, log_rx) = flume::bounded(100); - std::thread::spawn(move || { - for message in log_rx { - print_log_message(message, false, false); - } - }); + let dataflow_path = resolve_dataflow(dataflow).context("could not resolve dataflow")?; + let dataflow_session = + DataflowSession::read_session(&dataflow_path).context("failed to read DataflowSession")?; + let rt = Builder::new_multi_thread() + .enable_all() + .build() + .context("tokio runtime failed")?; - let result = rt.block_on(Daemon::run_dataflow( - &dataflow_path, - dataflow_session.build_id, - dataflow_session.local_build, - dataflow_session.session_id, - self.uv, - LogDestination::Channel { sender: log_tx }, - ))?; - handle_dataflow_result(result, None) + let (log_tx, log_rx) = flume::bounded(100); + std::thread::spawn(move || { + for message in log_rx { + print_log_message(message, false, false); + } + }); + + let result = rt.block_on(Daemon::run_dataflow( + &dataflow_path, + dataflow_session.build_id, + dataflow_session.local_build, + dataflow_session.session_id, + uv, + LogDestination::Channel { sender: log_tx }, + ))?; + handle_dataflow_result(result, None) +} + +impl Executable for Run { + fn execute(self) -> eyre::Result<()> { + run_func(self.dataflow, self.uv) } } diff --git a/binaries/cli/src/lib.rs b/binaries/cli/src/lib.rs index 03474839..868d7a5a 100644 --- a/binaries/cli/src/lib.rs +++ b/binaries/cli/src/lib.rs @@ -12,6 +12,8 @@ pub mod output; pub mod session; mod template; +pub use command::run_func; + const LOCALHOST: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); const LISTEN_WILDCARD: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); From 810ae047cfbfd35f58c7deebb01740cf7f12cd19 Mon Sep 17 00:00:00 2001 From: sjfhsjfh Date: Wed, 9 Jul 2025 00:23:01 +0800 Subject: [PATCH 35/40] chore: fmt --- binaries/cli/src/common.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/binaries/cli/src/common.rs b/binaries/cli/src/common.rs index 26431349..e02a1673 100644 --- a/binaries/cli/src/common.rs +++ b/binaries/cli/src/common.rs @@ -97,7 +97,9 @@ pub(crate) fn local_working_dir( ) } -pub(crate) fn cli_and_daemon_on_same_machine(session: &mut TcpRequestReplyConnection) -> eyre::Result { +pub(crate) fn cli_and_daemon_on_same_machine( + session: &mut TcpRequestReplyConnection, +) -> eyre::Result { let reply_raw = session .request(&serde_json::to_vec(&ControlRequest::CliAndDefaultDaemonOnSameMachine).unwrap()) .wrap_err("failed to send start dataflow message")?; From 37ea05439ace4a1e0a3ebea5c947ff3b568a92f7 Mon Sep 17 00:00:00 2001 From: drindr Date: Sun, 13 Jul 2025 23:49:36 +0800 Subject: [PATCH 36/40] fix: in c++ ros2 example, too few ticks to ensure everything work correctly --- examples/c++-ros2-dataflow/node-rust-api/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/c++-ros2-dataflow/node-rust-api/main.cc b/examples/c++-ros2-dataflow/node-rust-api/main.cc index 6fae1c34..ec5aeabd 100644 --- a/examples/c++-ros2-dataflow/node-rust-api/main.cc +++ b/examples/c++-ros2-dataflow/node-rust-api/main.cc @@ -73,7 +73,7 @@ int main() std::cerr << "Unknown event type " << static_cast(ty) << std::endl; } - if (received_ticks > 20) + if (received_ticks > 20 && responses_received > 0) { break; } From 5f8ee04aedebdc3acc6ff10077ebd6df4d77a083 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 14 Jul 2025 13:40:30 +0200 Subject: [PATCH 37/40] Fix buffer not printing because it contains legacy ERROR message --- binaries/daemon/src/spawn.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/binaries/daemon/src/spawn.rs b/binaries/daemon/src/spawn.rs index 2ecc5c1a..c4a3e285 100644 --- a/binaries/daemon/src/spawn.rs +++ b/binaries/daemon/src/spawn.rs @@ -430,19 +430,6 @@ impl PreparedNode { } }; - if buffer.contains("TRACE") - || buffer.contains("INFO") - || buffer.contains("DEBUG") - || buffer.contains("WARN") - || buffer.contains("ERROR") - { - // tracing output, potentially multi-line -> keep reading following lines - // until double-newline - if !buffer.ends_with("\n\n") && !finished { - continue; - } - } - // send the buffered lines let lines = std::mem::take(&mut buffer); let sent = stdout_tx.send(lines.clone()).await; From d03b6d138b9c8074b1f7f04df2bee91dc23d3dec Mon Sep 17 00:00:00 2001 From: haixuantao Date: Mon, 14 Jul 2025 13:41:38 +0200 Subject: [PATCH 38/40] Add pyo3 special method --- Cargo.lock | 47 +++++++++++++++++++ apis/python/node/Cargo.toml | 2 +- apis/python/node/src/lib.rs | 4 +- .../extensions/ros2-bridge/python/Cargo.toml | 2 +- .../extensions/ros2-bridge/python/src/lib.rs | 15 +++--- .../extensions/ros2-bridge/python/src/qos.rs | 11 ++--- 6 files changed, 62 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ff733ad..f86715e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3230,6 +3230,7 @@ dependencies = [ "futures", "pyo3", "pyo3-build-config", + "pyo3_special_method_derive", "pythonize", "serde_yaml 0.9.34+deprecated", "tokio", @@ -3414,6 +3415,7 @@ dependencies = [ "eyre", "futures", "pyo3", + "pyo3_special_method_derive", "serde", "serde_assert", ] @@ -8992,6 +8994,29 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "pyo3_special_method_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b75ccfc0eef7c0478db8aa23d23e97b39c74e29da21d9bf1e1934e63ad0709d" +dependencies = [ + "pyo3", + "pyo3_special_method_derive_macro", +] + +[[package]] +name = "pyo3_special_method_derive_macro" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc6e17b94c919f8295729dcddd368b7cd78231c39a3c23cdeec77b5834010cf" +dependencies = [ + "proc-macro2", + "pyo3", + "quote", + "quote_into", + "syn 2.0.101", +] + [[package]] name = "python3-dll-a" version = "0.2.13" @@ -9178,6 +9203,28 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quote_into" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93886ed56f228a5d960fc4d26afa3736df12a251872869cf24f5efe5f07699b9" +dependencies = [ + "proc-macro2", + "quote", + "quote_into_macro", +] + +[[package]] +name = "quote_into_macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36b828998c40452b5afe441c75194e93181432e669585f4ceb7b0d32a3f73525" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "r-efi" version = "5.2.0" diff --git a/apis/python/node/Cargo.toml b/apis/python/node/Cargo.toml index f03a4036..58832ffd 100644 --- a/apis/python/node/Cargo.toml +++ b/apis/python/node/Cargo.toml @@ -30,7 +30,7 @@ arrow = { workspace = true, features = ["pyarrow"] } pythonize = { workspace = true } futures = "0.3.28" dora-ros2-bridge-python = { workspace = true } -# pyo3_special_method_derive = "0.4.2" +pyo3_special_method_derive = "0.4.3" tokio = { version = "1.24.2", features = ["rt"] } [build-dependencies] diff --git a/apis/python/node/src/lib.rs b/apis/python/node/src/lib.rs index 6b279ca1..a203f0ba 100644 --- a/apis/python/node/src/lib.rs +++ b/apis/python/node/src/lib.rs @@ -17,7 +17,7 @@ use eyre::Context; use futures::{Stream, StreamExt}; use pyo3::prelude::*; use pyo3::types::{PyBytes, PyDict}; -/// use pyo3_special_method_derive::{Dict, Dir, Repr, Str}; +use pyo3_special_method_derive::{Dict, Dir, Repr, Str}; /// The custom node API lets you integrate `dora` into your application. /// It allows you to retrieve input and send output in any fashion you want. @@ -32,7 +32,7 @@ use pyo3::types::{PyBytes, PyDict}; /// /// :type node_id: str, optional #[pyclass] -/// #[derive(Dir, Dict, Str, Repr)] +#[derive(Dir, Dict, Str, Repr)] pub struct Node { events: Events, node: DelayedCleanup, diff --git a/libraries/extensions/ros2-bridge/python/Cargo.toml b/libraries/extensions/ros2-bridge/python/Cargo.toml index 9d8f94cb..b9253fb7 100644 --- a/libraries/extensions/ros2-bridge/python/Cargo.toml +++ b/libraries/extensions/ros2-bridge/python/Cargo.toml @@ -12,7 +12,7 @@ eyre = "0.6" serde = "1.0.166" arrow = { workspace = true, features = ["pyarrow"] } futures = "0.3.28" -# pyo3_special_method_derive = "0.4.2" +pyo3_special_method_derive = "0.4.3" [dev-dependencies] serde_assert = "0.7.1" diff --git a/libraries/extensions/ros2-bridge/python/src/lib.rs b/libraries/extensions/ros2-bridge/python/src/lib.rs index a8c206b9..14984245 100644 --- a/libraries/extensions/ros2-bridge/python/src/lib.rs +++ b/libraries/extensions/ros2-bridge/python/src/lib.rs @@ -18,7 +18,7 @@ use pyo3::{ types::{PyAnyMethods, PyDict, PyList, PyModule, PyModuleMethods}, Bound, PyAny, PyObject, PyResult, Python, }; -/// use pyo3_special_method_derive::{Dict, Dir, Repr, Str}; +use pyo3_special_method_derive::{Dict, Dir, Repr, Str}; use typed::{deserialize::StructDeserializer, TypeInfo, TypedValue}; pub mod qos; @@ -46,7 +46,7 @@ pub mod typed; /// :type ros_paths: typing.List[str], optional /// #[pyclass] -/// #[derive(Str, Repr, Dir, Dict)] +#[derive(Str, Repr, Dir, Dict)] pub struct Ros2Context { context: ros2_client::Context, messages: Arc>>, @@ -150,7 +150,7 @@ impl Ros2Context { /// See: https://github.com/jhelovuo/ros2-client/issues/4 /// #[pyclass] -/// #[derive(Str, Repr, Dir, Dict)] +#[derive(Str, Repr, Dir, Dict)] pub struct Ros2Node { node: ros2_client::Node, messages: Arc>>, @@ -257,8 +257,7 @@ impl Ros2Node { /// ROS2 Node Options /// :type rosout: bool, optional /// -#[derive(Clone, Default)] -/// , Str, Repr, Dir, Dict)] +#[derive(Clone, Default, Str, Repr, Dir, Dict)] #[pyclass] #[non_exhaustive] pub struct Ros2NodeOptions { @@ -289,7 +288,7 @@ impl From for ros2_client::NodeOptions { /// - dora Ros2 bridge functionality is considered **unstable**. It may be changed /// at any point without it being considered a breaking change. #[pyclass] -/// #[derive(Str, Repr, Dir, Dict)] +#[derive(Str, Repr, Dir, Dict)] #[non_exhaustive] pub struct Ros2Topic { topic: rustdds::Topic, @@ -302,7 +301,7 @@ pub struct Ros2Topic { /// - dora Ros2 bridge functionality is considered **unstable**. It may be changed /// at any point without it being considered a breaking change. #[pyclass] -/// #[derive(Str, Repr, Dir, Dict)] +#[derive(Str, Repr, Dir, Dict)] #[non_exhaustive] pub struct Ros2Publisher { publisher: ros2_client::Publisher>, @@ -373,7 +372,7 @@ impl Ros2Publisher { /// - dora Ros2 bridge functionality is considered **unstable**. It may be changed /// at any point without it being considered a breaking change. #[pyclass] -/// #[derive(Str, Repr, Dir, Dict)] +#[derive(Str, Repr, Dir, Dict)] #[non_exhaustive] pub struct Ros2Subscription { deserializer: StructDeserializer<'static>, diff --git a/libraries/extensions/ros2-bridge/python/src/qos.rs b/libraries/extensions/ros2-bridge/python/src/qos.rs index 24c10cbc..315b8ec1 100644 --- a/libraries/extensions/ros2-bridge/python/src/qos.rs +++ b/libraries/extensions/ros2-bridge/python/src/qos.rs @@ -1,6 +1,6 @@ use ::dora_ros2_bridge::rustdds::{self, policy}; use pyo3::prelude::{pyclass, pymethods}; -/// use pyo3_special_method_derive::{Dict, Dir, Repr, Str}; +use pyo3_special_method_derive::{Dict, Dir, Repr, Str}; /// ROS2 QoS Policy /// @@ -13,8 +13,7 @@ use pyo3::prelude::{pyclass, pymethods}; /// :type keep_last: int, optional /// :rtype: dora.Ros2QoSPolicies /// -#[derive(Clone)] -/// , Str, Repr, Dir, Dict)] +#[derive(Clone, Str, Repr, Dir, Dict)] #[pyclass] #[non_exhaustive] pub struct Ros2QosPolicies { @@ -80,8 +79,7 @@ impl From for rustdds::QosPolicies { /// DDS 2.2.3.4 DURABILITY /// /// :rtype: dora.Ros2Durability -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// , Str, Repr, Dir, Dict)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Str, Repr, Dir, Dict)] #[pyclass(eq, eq_int)] pub enum Ros2Durability { Volatile, @@ -107,8 +105,7 @@ impl From for policy::Durability { /// DDS 2.2.3.11 LIVELINESS /// :rtype: dora.Ros2Liveliness -#[derive(Copy, Clone, PartialEq)] -/// , Str, Repr, Dir, Dict)] +#[derive(Copy, Clone, PartialEq, Str, Repr, Dir, Dict)] #[pyclass(eq, eq_int)] pub enum Ros2Liveliness { Automatic, From cef799dffff6a705d8d54cb4bc2333d21e250220 Mon Sep 17 00:00:00 2001 From: haixuantao Date: Tue, 15 Jul 2025 13:46:02 +0200 Subject: [PATCH 39/40] Fix weird daemon folder --- binaries/daemon/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/binaries/daemon/src/lib.rs b/binaries/daemon/src/lib.rs index d23dd2b8..e88359e5 100644 --- a/binaries/daemon/src/lib.rs +++ b/binaries/daemon/src/lib.rs @@ -2158,9 +2158,7 @@ impl Daemon { // use subfolder of daemon working dir let daemon_working_dir = current_dir().context("failed to get daemon working dir")?; - Ok(daemon_working_dir - .join("_work") - .join(session_id.uuid().to_string())) + Ok(daemon_working_dir) } } } From 16ec7fbaeea58ec159e1d5f2e9db310652f67419 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 15 Jul 2025 16:37:35 +0200 Subject: [PATCH 40/40] Revert "Fix weird daemon folder" --- binaries/daemon/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/binaries/daemon/src/lib.rs b/binaries/daemon/src/lib.rs index e88359e5..d23dd2b8 100644 --- a/binaries/daemon/src/lib.rs +++ b/binaries/daemon/src/lib.rs @@ -2158,7 +2158,9 @@ impl Daemon { // use subfolder of daemon working dir let daemon_working_dir = current_dir().context("failed to get daemon working dir")?; - Ok(daemon_working_dir) + Ok(daemon_working_dir + .join("_work") + .join(session_id.uuid().to_string())) } } }