diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c098ef18..27d0441b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,6 +118,9 @@ jobs: - name: "Rust Dataflow example" timeout-minutes: 30 run: cargo run --example rust-dataflow + - name: "Rust Git Dataflow example" + timeout-minutes: 30 + run: cargo run --example rust-dataflow-git - name: "Multiple Daemons example" timeout-minutes: 30 run: cargo run --example multiple-daemons @@ -194,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 @@ -209,11 +212,11 @@ jobs: source /opt/ros/humble/setup.bash && ros2 run turtlesim turtlesim_node & source /opt/ros/humble/setup.bash && ros2 run examples_rclcpp_minimal_service service_main & cargo run --example rust-ros2-dataflow --features="ros2-examples" - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 if: runner.os != 'Windows' with: python-version: "3.8" - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 if: runner.os == 'Windows' with: python-version: "3.10" @@ -321,7 +324,7 @@ jobs: dora stop --name ci-rust-dynamic --grace-duration 5s dora destroy - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: # TODO: Support Python 3.13 when https://github.com/pytorch/pytorch/issues/130249 is fixed python-version: "3.12" @@ -339,35 +342,42 @@ jobs: # Test Python template Project dora new test_python_project --lang python --internal-create-with-path-dependencies cd test_python_project - uv venv --seed -p 3.11 + uv venv --seed -p 3.12 uv pip install -e ../apis/python/node - dora build dataflow.yml --uv uv pip install ruff pytest + echo "Running dora up" + dora up + echo "Running dora build" + dora build dataflow.yml --uv + # Check Compliancy uv run ruff check . uv run pytest export OPERATING_MODE=SAVE - dora up + echo "Running dora list" dora list dora build dataflow.yml --uv echo "Running CI Python Test" dora start dataflow.yml --name ci-python-test --detach --uv sleep 10 + echo "Running dora stop" dora stop --name ci-python-test --grace-duration 5s dora destroy + sleep 5 cd .. # Run Python Node Example echo "Running Python Node Example" dora up - uv venv --seed -p 3.11 + uv venv --seed -p 3.12 uv pip install -e apis/python/node dora build examples/python-dataflow/dataflow.yml --uv dora start examples/python-dataflow/dataflow.yml --name ci-python --detach --uv sleep 10 + echo "Running dora stop" dora stop --name ci-python --grace-duration 30s # Run Python Dynamic Node Example @@ -376,15 +386,18 @@ jobs: dora start examples/python-dataflow/dataflow_dynamic.yml --name ci-python-dynamic --detach --uv uv run opencv-plot --name plot sleep 10 + echo "Running dora stop" dora stop --name ci-python-dynamic --grace-duration 30s # Run Python Operator Example echo "Running CI Operator Test" dora start examples/python-operator-dataflow/dataflow.yml --name ci-python-operator --detach --uv sleep 10 + echo "Running dora stop" dora stop --name ci-python-operator --grace-duration 30s dora destroy + sleep 5 # Run Python queue latency test echo "Running CI Queue Latency Test" 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/.github/workflows/pip-release.yml b/.github/workflows/pip-release.yml index 98465b85..711698fb 100644 --- a/.github/workflows/pip-release.yml +++ b/.github/workflows/pip-release.yml @@ -66,6 +66,7 @@ jobs: args: --release --out dist --zig manylinux: manylinux_2_28 working-directory: ${{ matrix.repository.path }} + before-script-linux: sudo apt-get install libatomic1-i386-cross libatomic1-armhf-cross && mkdir -p $HOME/.rustup/toolchains/1.84-x86_64-unknown-linux-gnu/lib/rustlib/i686-unknown-linux-gnu/lib/ && ln -s /usr/i686-linux-gnu/lib/libatomic.so.1 $HOME/.rustup/toolchains/1.84-x86_64-unknown-linux-gnu/lib/rustlib/i686-unknown-linux-gnu/lib/libatomic.so && ln -s /usr/i686-linux-gnu/lib/libatomic.so.1 $HOME/.rustup/toolchains/1.84-x86_64-unknown-linux-gnu/lib/rustlib/i686-unknown-linux-gnu/lib/libatomic.so.1 && ln -s /usr/i686-linux-gnu/lib/libatomic.so.1 /opt/hostedtoolcache/Python/3.8.18/x64/lib/libatomic.so.1 && mkdir -p $HOME/.rustup/toolchains/1.84-x86_64-unknown-linux-gnu/lib/rustlib/armv7-unknown-linux-gnueabihf/lib/ && ln -s /usr/arm-linux-gnueabihf/lib/libatomic.so.1 $HOME/.rustup/toolchains/1.84-x86_64-unknown-linux-gnu/lib/rustlib/armv7-unknown-linux-gnueabihf/lib/libatomic.so - name: Upload wheels if: github.event_name == 'release' uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 1942c227..1ff55fc9 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,7 @@ __pycache__/ # Distribution / packaging .Python -build/ +/build/ develop-eggs/ dist/ downloads/ diff --git a/Cargo.lock b/Cargo.lock index 103ff25e..da30dfbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ checksum = "8b5ace29ee3216de37c0546865ad08edef58b0f9e76838ed8959a84a990e58c5" [[package]] name = "ab_glyph" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" [[package]] name = "accelerate-src" @@ -71,7 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f47983a1084940ba9a39c077a8c63e55c619388be5476ac04c804cfbd1e63459" dependencies = [ "accesskit", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "immutable-chunkmap", ] @@ -83,7 +83,7 @@ checksum = "7329821f3bd1101e03a7d2e03bd339e3ac0dc64c70b4c9f9ae1949e3ba8dece1" dependencies = [ "accesskit", "accesskit_consumer", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -97,7 +97,7 @@ checksum = "fcee751cc20d88678c33edaf9c07e8b693cd02819fe89053776f5313492273f5" dependencies = [ "accesskit", "accesskit_atspi_common", - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-task", "atspi", @@ -115,7 +115,7 @@ checksum = "24fcd5d23d70670992b823e735e859374d694a3d12bfd8dd32bd3bd8bedb5d81" dependencies = [ "accesskit", "accesskit_consumer", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "paste", "static_assertions", "windows 0.58.0", @@ -147,9 +147,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" @@ -157,7 +157,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "cipher", "cpufeatures", ] @@ -168,13 +168,13 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "const-random", "getrandom 0.3.3", "once_cell", "serde", "version_check", - "zerocopy 0.8.25", + "zerocopy 0.8.26", ] [[package]] @@ -201,6 +201,16 @@ dependencies = [ "serde", ] +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", + "serde", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -284,9 +294,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -299,33 +309,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", @@ -341,6 +351,12 @@ dependencies = [ "backtrace", ] +[[package]] +name = "apodize" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca387cdc0a1f9c7a7c26556d584aa2d07fc529843082e4861003cde4ab914ed" + [[package]] name = "approx" version = "0.5.1" @@ -361,9 +377,9 @@ dependencies = [ [[package]] name = "arboard" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" +checksum = "55f533f8e0af236ffe5eb979b99381df3258853f00ba2e44b6e1955292c75227" dependencies = [ "clipboard-win", "image", @@ -393,7 +409,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -465,7 +481,7 @@ dependencies = [ "arrow-schema", "chrono", "half", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "num", ] @@ -554,7 +570,7 @@ dependencies = [ "arrow-schema", "chrono", "half", - "indexmap 2.9.0", + "indexmap 2.10.0", "lexical-core", "memchr", "num", @@ -673,7 +689,7 @@ dependencies = [ "serde", "serde_repr", "url", - "zbus 5.7.1", + "zbus 5.8.0", ] [[package]] @@ -689,7 +705,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror 1.0.69", - "time 0.3.41", + "time", ] [[package]] @@ -700,7 +716,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -712,7 +728,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -756,9 +772,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -797,7 +813,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-io 2.4.1", "async-lock 3.4.0", @@ -815,7 +831,7 @@ checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock 2.8.0", "autocfg", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "concurrent-queue", "futures-lite 1.13.0", "log", @@ -834,7 +850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" dependencies = [ "async-lock 3.4.0", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "concurrent-queue", "futures-io", "futures-lite 2.6.0", @@ -887,7 +903,7 @@ dependencies = [ "async-lock 2.8.0", "async-signal", "blocking", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "event-listener 3.1.0", "futures-lite 1.13.0", "rustix 0.38.44", @@ -900,13 +916,13 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-io 2.4.1", "async-lock 3.4.0", "async-signal", "async-task", "blocking", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "event-listener 5.4.0", "futures-lite 2.6.0", "rustix 1.0.7", @@ -921,7 +937,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -944,7 +960,7 @@ dependencies = [ "async-io 2.4.1", "async-lock 3.4.0", "atomic-waker", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "futures-core", "futures-io", "rustix 1.0.7", @@ -1000,7 +1016,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1017,7 +1033,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1052,7 +1068,7 @@ checksum = "49c98dba06b920588de7d63f6acc23f1e6a9fade5fd6198e564506334fb5a4f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1119,9 +1135,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av-data" @@ -1168,9 +1184,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" dependencies = [ "arrayvec", ] @@ -1196,7 +1212,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower 0.5.2", "tower-layer", "tower-service", @@ -1217,7 +1233,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", ] @@ -1235,7 +1251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "miniz_oxide", "object", @@ -1263,9 +1279,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bat" @@ -1277,7 +1293,7 @@ dependencies = [ "bincode", "bugreport", "bytesize", - "clap 4.5.39", + "clap 4.5.40", "clircle", "console", "content_inspector", @@ -1293,7 +1309,7 @@ dependencies = [ "path_abs", "plist", "regex", - "semver 1.0.26", + "semver", "serde", "serde_yaml 0.9.34+deprecated", "shell-words", @@ -1306,7 +1322,7 @@ dependencies = [ [[package]] name = "benchmark-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -1319,7 +1335,7 @@ dependencies = [ [[package]] name = "benchmark-example-sink" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -1402,9 +1418,9 @@ checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] name = "bitstream-io" -version = "4.1.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a4952b106ee0920c957fc00cf32ddb1e885c5d8beee8f66ab973458129ff5b" +checksum = "1f30faa215941f112abe3f87ae44889e4596260602a1a2b82837c7860fdd56a0" dependencies = [ "core2", ] @@ -1444,11 +1460,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", "futures-lite 2.6.0", @@ -1457,9 +1473,9 @@ dependencies = [ [[package]] name = "bm25" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9874599901ae2aaa19b1485145be2fa4e9af42d1b127672a03a7099ab6350bac" +checksum = "1036029224bd72581186b629168952596c4964686dcdd59bccd810a7be1f5843" dependencies = [ "cached", "deunicode", @@ -1477,7 +1493,18 @@ checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 4.0.3", +] + +[[package]] +name = "brotli" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 5.0.0", ] [[package]] @@ -1490,6 +1517,16 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "1.12.0" @@ -1523,9 +1560,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-slice-cast" @@ -1541,9 +1578,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" dependencies = [ "bytemuck_derive", ] @@ -1556,7 +1593,7 @@ checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1637,7 +1674,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1684,7 +1721,7 @@ dependencies = [ [[package]] name = "candle-core" version = "0.8.0" -source = "git+https://github.com/EricLBuehler/candle.git?rev=34e5753b#34e5753b79ad2ac2300c86e4a4266a8214613c18" +source = "git+https://github.com/EricLBuehler/candle.git?rev=98c0436e#98c0436eaf55ea91fb34fd3aa96f557990a5ba40" dependencies = [ "accelerate-src", "byteorder", @@ -1711,7 +1748,7 @@ dependencies = [ [[package]] name = "candle-flash-attn" version = "0.8.0" -source = "git+https://github.com/EricLBuehler/candle.git?rev=34e5753b#34e5753b79ad2ac2300c86e4a4266a8214613c18" +source = "git+https://github.com/EricLBuehler/candle.git?rev=98c0436e#98c0436eaf55ea91fb34fd3aa96f557990a5ba40" dependencies = [ "anyhow", "bindgen_cuda 0.1.5", @@ -1722,7 +1759,7 @@ dependencies = [ [[package]] name = "candle-kernels" version = "0.8.0" -source = "git+https://github.com/EricLBuehler/candle.git?rev=34e5753b#34e5753b79ad2ac2300c86e4a4266a8214613c18" +source = "git+https://github.com/EricLBuehler/candle.git?rev=98c0436e#98c0436eaf55ea91fb34fd3aa96f557990a5ba40" dependencies = [ "bindgen_cuda 0.1.5", ] @@ -1730,7 +1767,7 @@ dependencies = [ [[package]] name = "candle-metal-kernels" version = "0.8.0" -source = "git+https://github.com/EricLBuehler/candle.git?rev=34e5753b#34e5753b79ad2ac2300c86e4a4266a8214613c18" +source = "git+https://github.com/EricLBuehler/candle.git?rev=98c0436e#98c0436eaf55ea91fb34fd3aa96f557990a5ba40" dependencies = [ "metal 0.27.0", "once_cell", @@ -1741,7 +1778,7 @@ dependencies = [ [[package]] name = "candle-nn" version = "0.8.0" -source = "git+https://github.com/EricLBuehler/candle.git?rev=34e5753b#34e5753b79ad2ac2300c86e4a4266a8214613c18" +source = "git+https://github.com/EricLBuehler/candle.git?rev=98c0436e#98c0436eaf55ea91fb34fd3aa96f557990a5ba40" dependencies = [ "accelerate-src", "candle-core", @@ -1772,7 +1809,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.26", + "semver", "serde", "serde_json", ] @@ -1785,17 +1822,26 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.26", + "semver", "serde", "serde_json", "thiserror 1.0.69", ] +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" -version = "1.2.25" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "jobserver", "libc", @@ -1866,9 +1912,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34e221e91c7eb5e8315b5c9cf1a61670938c0626451f954a51693ed44b37f45" +checksum = "0d0390889d58f934f01cd49736275b4c2da15bcfc328c78ff2349907e6cabf22" dependencies = [ "smallvec", "target-lexicon 0.13.2", @@ -1882,9 +1928,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -1898,7 +1944,7 @@ version = "0.13.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe45e18904af7af10e4312df7c97251e98af98c70f42f1f2587aecfcbee56bf" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "lazy_static", "num-traits", "regex", @@ -1989,34 +2035,34 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", - "clap_derive 4.5.32", + "clap_derive 4.5.40", ] [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.4", + "clap_lex 0.7.5", "strsim 0.11.1", "terminal_size", ] [[package]] name = "clap_complete" -version = "4.5.52" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a554639e42d0c838336fc4fbedb9e2df3ad1fa4acda149f9126b4ccfcd7900f" +checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677" dependencies = [ - "clap 4.5.39", + "clap 4.5.40", ] [[package]] @@ -2034,14 +2080,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2055,9 +2101,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clean-path" @@ -2080,7 +2126,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8e87cbed5354f17bd8ca8821a097fb62599787fe8f611743fad7ee156a0a600" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "serde", "winapi 0.3.9", @@ -2104,7 +2150,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.2.0", + "unicode-width 0.2.1", ] [[package]] @@ -2121,9 +2167,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" @@ -2153,12 +2199,12 @@ checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" dependencies = [ "crossterm 0.28.1", "unicode-segmentation", - "unicode-width 0.2.0", + "unicode-width 0.2.1", ] [[package]] name = "communication-layer-pub-sub" -version = "0.3.11" +version = "0.3.12" dependencies = [ "flume 0.10.14", "zenoh 0.7.0-rc", @@ -2166,7 +2212,22 @@ dependencies = [ [[package]] name = "communication-layer-request-reply" -version = "0.3.11" +version = "0.3.12" + +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if 1.0.1", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", +] [[package]] name = "concat-idents" @@ -2175,7 +2236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2196,7 +2257,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", + "unicode-width 0.2.1", "windows-sys 0.59.0", ] @@ -2357,13 +2418,28 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", ] [[package]] @@ -2472,9 +2548,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -2506,7 +2582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2556,11 +2632,38 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.1", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "cxx" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71ea7f29c73f7ffa64c50b83c9fe4d3a6d4be89a86b009eb80d5a6d3429d741" +checksum = "be1149bab7a5580cb267215751389597c021bfad13c0bb00c54e19559333764c" dependencies = [ "cc", "cxxbridge-cmd", @@ -2572,47 +2675,50 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a8232661d66dcf713394726157d3cfe0a89bfc85f52d6e9f9bbc2306797fe7" +checksum = "6aeeaf1aefae8e0f5141920a7ecbc64a22ab038d4b4ac59f2d19e0effafd5b53" dependencies = [ "cc", "codespan-reporting 0.12.0", + "indexmap 2.10.0", "proc-macro2", "quote", "scratch", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "cxxbridge-cmd" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f44296c8693e9ea226a48f6a122727f77aa9e9e338380cb021accaeeb7ee279" +checksum = "c36ac1f9a72064b1f41fd7b49a4c1b3bf33b9ccb1274874dda6d264f57c55964" dependencies = [ - "clap 4.5.39", + "clap 4.5.40", "codespan-reporting 0.12.0", + "indexmap 2.10.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "cxxbridge-flags" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f69c181c176981ae44ba9876e2ea41ce8e574c296b38d06925ce9214fb8e4" +checksum = "170c6ff5d009663866857a91ebee55b98ea4d4b34e7d7aba6dc4a4c95cc7b748" [[package]] name = "cxxbridge-macro" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8faff5d4467e0709448187df29ccbf3b0982cc426ee444a193f87b11afb565a8" +checksum = "4984a142211026786011a7e79fa22faa1eca1e9cbf0e60bffecfd57fd3db88f1" dependencies = [ + "indexmap 2.10.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2660,7 +2766,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2682,7 +2788,16 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "dary_heap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" +dependencies = [ + "serde", ] [[package]] @@ -2691,7 +2806,7 @@ version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "hashbrown 0.14.5", "lock_api", "once_cell", @@ -2792,7 +2907,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2803,7 +2918,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2824,7 +2939,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2834,7 +2949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2845,7 +2960,27 @@ checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -2857,7 +2992,7 @@ dependencies = [ "anyhow", "bytemuck", "bytemuck_derive", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "regex-syntax 0.8.5", "strum 0.27.1", ] @@ -2904,15 +3039,6 @@ dependencies = [ "dirs-sys 0.3.7", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - [[package]] name = "dirs" version = "6.0.0" @@ -2954,7 +3080,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2993,7 +3119,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3022,7 +3148,7 @@ dependencies = [ [[package]] name = "dora-arrow-convert" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "chrono", @@ -3033,10 +3159,10 @@ dependencies = [ [[package]] name = "dora-cli" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bat", - "clap 4.5.39", + "clap 4.5.40", "colored", "communication-layer-request-reply", "ctrlc", @@ -3049,14 +3175,17 @@ dependencies = [ "dora-operator-api-c", "dora-runtime", "dora-tracing", + "dunce", "duration-str", "env_logger 0.11.8", "eyre", "futures", + "git2", "inquire", "log", "notify 5.2.0", "pyo3", + "pyo3-build-config", "self-replace", "self_update", "serde", @@ -3067,13 +3196,14 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "tracing-log 0.2.0", "uuid 1.17.0", "webbrowser 0.8.15", ] [[package]] name = "dora-coordinator" -version = "0.3.11" +version = "0.3.12" dependencies = [ "ctrlc", "dora-core", @@ -3094,28 +3224,33 @@ dependencies = [ [[package]] name = "dora-core" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-message", + "dunce", "eyre", + "fs_extra", + "git2", + "itertools 0.14.0", "log", "once_cell", - "schemars", + "schemars 0.8.22", "serde", "serde-with-expand-env", "serde_json", "serde_yaml 0.9.34+deprecated", "tokio", "tracing", + "url", "uuid 1.17.0", "which", ] [[package]] name = "dora-daemon" -version = "0.3.11" +version = "0.3.12" dependencies = [ - "aligned-vec", + "aligned-vec 0.5.0", "async-trait", "bincode", "crossbeam", @@ -3127,18 +3262,22 @@ dependencies = [ "dora-message", "dora-node-api", "dora-tracing", + "dunce", "eyre", "flume 0.10.14", "futures", "futures-concurrency", + "git2", + "itertools 0.14.0", "serde_json", - "serde_yaml 0.8.26", + "serde_yaml 0.9.34+deprecated", "shared-memory-server", "sysinfo 0.30.13", "tokio", "tokio-stream", "tracing", "tracing-opentelemetry", + "url", "uuid 1.17.0", "which", "zenoh 1.4.0", @@ -3146,7 +3285,7 @@ dependencies = [ [[package]] name = "dora-dav1d" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bitstream-io 2.6.0", "bytemuck", @@ -3160,10 +3299,10 @@ dependencies = [ [[package]] name = "dora-download" -version = "0.3.11" +version = "0.3.12" dependencies = [ "eyre", - "reqwest 0.12.19", + "reqwest", "tokio", "tracing", ] @@ -3172,6 +3311,7 @@ dependencies = [ name = "dora-examples" version = "0.0.0" dependencies = [ + "dora-cli", "dora-coordinator", "dora-core", "dora-download", @@ -3189,7 +3329,7 @@ dependencies = [ [[package]] name = "dora-kit-car" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "dotenv", @@ -3203,17 +3343,17 @@ dependencies = [ [[package]] name = "dora-message" -version = "0.4.4" +version = "0.5.0" dependencies = [ - "aligned-vec", + "aligned-vec 0.5.0", "arrow-data", "arrow-schema", "bincode", "eyre", "log", "once_cell", - "schemars", - "semver 1.0.26", + "schemars 0.8.22", + "semver", "serde", "serde-with-expand-env", "serde_yaml 0.9.34+deprecated", @@ -3224,7 +3364,7 @@ dependencies = [ [[package]] name = "dora-metrics" -version = "0.3.11" +version = "0.3.12" dependencies = [ "eyre", "opentelemetry 0.29.1", @@ -3245,9 +3385,9 @@ dependencies = [ [[package]] name = "dora-node-api" -version = "0.3.11" +version = "0.3.12" dependencies = [ - "aligned-vec", + "aligned-vec 0.5.0", "arrow", "bincode", "dora-arrow-convert", @@ -3261,7 +3401,7 @@ dependencies = [ "futures-concurrency", "futures-timer", "serde_json", - "serde_yaml 0.8.26", + "serde_yaml 0.9.34+deprecated", "shared-memory-server", "shared_memory_extended", "tokio", @@ -3270,7 +3410,7 @@ dependencies = [ [[package]] name = "dora-node-api-c" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow-array", "dora-node-api", @@ -3280,7 +3420,7 @@ dependencies = [ [[package]] name = "dora-node-api-cxx" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "cxx", @@ -3298,10 +3438,10 @@ dependencies = [ [[package]] name = "dora-node-api-python" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", - "dora-daemon", + "dora-cli", "dora-download", "dora-node-api", "dora-operator-api-python", @@ -3311,14 +3451,15 @@ dependencies = [ "flume 0.10.14", "futures", "pyo3", + "pyo3-build-config", "pythonize", - "serde_yaml 0.8.26", + "serde_yaml 0.9.34+deprecated", "tokio", ] [[package]] name = "dora-object-to-pose" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -3327,14 +3468,14 @@ dependencies = [ [[package]] name = "dora-openai-proxy-server" -version = "0.3.11" +version = "0.3.12" dependencies = [ "chrono", "dora-node-api", "eyre", "futures", "hyper 0.14.32", - "indexmap 2.9.0", + "indexmap 2.10.0", "mime_guess", "serde", "serde_json", @@ -3348,7 +3489,7 @@ dependencies = [ [[package]] name = "dora-operator-api" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-arrow-convert", "dora-operator-api-macros", @@ -3357,14 +3498,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", @@ -3373,7 +3514,7 @@ dependencies = [ [[package]] name = "dora-operator-api-macros" -version = "0.3.11" +version = "0.3.12" dependencies = [ "proc-macro2", "quote", @@ -3382,9 +3523,9 @@ dependencies = [ [[package]] name = "dora-operator-api-python" -version = "0.3.11" +version = "0.3.12" dependencies = [ - "aligned-vec", + "aligned-vec 0.5.0", "arrow", "arrow-schema", "dora-node-api", @@ -3393,12 +3534,12 @@ dependencies = [ "futures", "futures-concurrency", "pyo3", - "serde_yaml 0.8.26", + "serde_yaml 0.9.34+deprecated", ] [[package]] name = "dora-operator-api-types" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "dora-arrow-convert", @@ -3407,12 +3548,13 @@ dependencies = [ [[package]] name = "dora-rav1e" -version = "0.3.11+fix1" +version = "0.3.12" dependencies = [ "avif-serialize", "bytemuck", "dora-node-api", "eyre", + "little_exif", "log", "pyo3", "rav1e", @@ -3420,7 +3562,7 @@ dependencies = [ [[package]] name = "dora-record" -version = "0.3.11" +version = "0.3.12" dependencies = [ "chrono", "dora-node-api", @@ -3432,7 +3574,7 @@ dependencies = [ [[package]] name = "dora-rerun" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bytemuck", "dora-node-api", @@ -3447,7 +3589,7 @@ dependencies = [ [[package]] name = "dora-ros2-bridge" -version = "0.3.11" +version = "0.3.12" dependencies = [ "array-init", "dora-daemon", @@ -3470,7 +3612,7 @@ dependencies = [ [[package]] name = "dora-ros2-bridge-msg-gen" -version = "0.3.11" +version = "0.3.12" dependencies = [ "anyhow", "glob", @@ -3486,7 +3628,7 @@ dependencies = [ [[package]] name = "dora-ros2-bridge-python" -version = "0.3.11" +version = "0.3.12" dependencies = [ "arrow", "dora-ros2-bridge", @@ -3500,9 +3642,9 @@ dependencies = [ [[package]] name = "dora-runtime" -version = "0.3.11" +version = "0.3.12" dependencies = [ - "aligned-vec", + "aligned-vec 0.5.0", "arrow", "dora-core", "dora-download", @@ -3519,7 +3661,7 @@ dependencies = [ "libloading 0.7.4", "pyo3", "pythonize", - "serde_yaml 0.8.26", + "serde_yaml 0.9.34+deprecated", "tokio", "tokio-stream", "tracing", @@ -3539,7 +3681,7 @@ dependencies = [ [[package]] name = "dora-tracing" -version = "0.3.11" +version = "0.3.12" dependencies = [ "eyre", "opentelemetry 0.18.0", @@ -3599,7 +3741,7 @@ dependencies = [ "rust_decimal", "serde", "thiserror 1.0.69", - "time 0.3.41", + "time", ] [[package]] @@ -3636,6 +3778,31 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18aade80d5e09429040243ce1143ddc08a92d7a22820ac512610410a4dd5214f" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.2.0", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "signature 2.2.0", + "subtle", + "zeroize", +] + [[package]] name = "eframe" version = "0.31.1" @@ -3739,7 +3906,7 @@ dependencies = [ "serde", "smithay-clipboard", "web-time", - "webbrowser 1.0.4", + "webbrowser 1.0.5", "winit", ] @@ -3907,7 +4074,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", ] [[package]] @@ -3931,7 +4098,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3952,14 +4119,14 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -3967,13 +4134,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3984,28 +4151,28 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "enumset" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6b7c3d347de0a9f7bfd2f853be43fe32fa6fac30c70f6d6d67a1e936b87ee" +checksum = "d6ee17054f550fd7400e1906e2f9356c7672643ed34008a9e8abe147ccd2d821" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da3ea9e1d1a3b1593e15781f930120e72aa7501610b2f82e5b6739c72e8eac5" +checksum = "76d07902c93376f1e96c34abc4d507c0911df3816cef50b01f5a2ff3ad8c370d" dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4071,19 +4238,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7e7a64c02cf7a5b51e745a9e45f60660a286f151c238b9d397b3e923f5082f" [[package]] -name = "equivalent" -version = "1.0.2" +name = "equator" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] [[package]] -name = "errno" -version = "0.3.12" +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4113,7 +4300,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "home", "windows-sys 0.48.0", ] @@ -4191,6 +4378,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "extended" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" + [[package]] name = "extension-traits" version = "1.0.1" @@ -4281,13 +4474,19 @@ dependencies = [ "anyhow", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filetime" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "libredox", "windows-sys 0.59.0", @@ -4336,9 +4535,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -4418,7 +4617,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4436,6 +4635,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -4584,7 +4789,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4767,11 +4972,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" dependencies = [ "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "log", "rustversion", - "windows 0.61.1", + "windows 0.61.3", ] [[package]] @@ -4807,11 +5012,11 @@ dependencies = [ [[package]] name = "getopts" -version = "0.2.21" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" dependencies = [ - "unicode-width 0.1.14", + "unicode-width 0.2.1", ] [[package]] @@ -4820,10 +5025,10 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -4833,7 +5038,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "js-sys", "libc", "r-efi", @@ -4843,9 +5048,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ "color_quant", "weezl", @@ -4874,7 +5079,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4887,6 +5092,8 @@ dependencies = [ "libc", "libgit2-sys", "log", + "openssl-probe", + "openssl-sys", "url", ] @@ -4978,7 +5185,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5086,7 +5293,7 @@ checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ "bitflags 2.9.1", "gpu-descriptor-types", - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -5124,7 +5331,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -5133,9 +5340,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", @@ -5143,7 +5350,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -5157,7 +5364,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "bytemuck", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "crunchy", "num-traits", "rand 0.9.1", @@ -5182,9 +5389,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -5240,9 +5447,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -5268,25 +5475,25 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hf-hub" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc03dcb0b0a83ae3f3363ec811014ae669f083e4e499c66602f447c4828737a1" +checksum = "629d8f3bbeda9d148036d6b0de0a3ab947abd08ce90626327fc3547a49d59d97" dependencies = [ - "dirs 5.0.1", + "dirs 6.0.0", "futures", "http 1.3.1", - "indicatif 0.17.11", + "indicatif", "libc", "log", "num_cpus", - "rand 0.8.5", - "reqwest 0.12.19", + "rand 0.9.1", + "reqwest", "serde", "serde_json", "thiserror 2.0.12", "tokio", "ureq", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5307,6 +5514,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + [[package]] name = "html2text" version = "0.14.4" @@ -5316,7 +5529,7 @@ dependencies = [ "html5ever 0.31.0", "tendril", "thiserror 2.0.12", - "unicode-width 0.2.0", + "unicode-width 0.2.1", ] [[package]] @@ -5339,7 +5552,7 @@ checksum = "953cbbe631aae7fc0a112702ad5d3aaf09da38beaf45ea84610d6e1c358f569c" dependencies = [ "log", "mac", - "markup5ever 0.16.1", + "markup5ever 0.16.2", "match_token", ] @@ -5426,7 +5639,7 @@ dependencies = [ "http 1.3.1", "http-cache", "http-cache-semantics", - "reqwest 0.12.19", + "reqwest", "reqwest-middleware", "serde", "url", @@ -5441,7 +5654,7 @@ dependencies = [ "http 1.3.1", "http-serde", "serde", - "time 0.3.41", + "time", ] [[package]] @@ -5505,7 +5718,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.10", + "h2 0.4.11", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -5519,33 +5732,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.32", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.6" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", "tower-service", - "webpki-roots 1.0.0", + "webpki-roots 1.0.1", ] [[package]] @@ -5563,9 +5762,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "base64 0.22.1", "bytes", @@ -5580,7 +5779,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2 0.5.10", - "system-configuration 0.6.1", + "system-configuration", "tokio", "tower-service", "tracing", @@ -5756,9 +5955,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" dependencies = [ "byteorder-lite", "quick-error", @@ -5804,27 +6003,15 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "serde", ] -[[package]] -name = "indicatif" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" -dependencies = [ - "console", - "lazy_static", - "number_prefix 0.3.0", - "regex", -] - [[package]] name = "indicatif" version = "0.17.11" @@ -5832,10 +6019,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" dependencies = [ "console", - "number_prefix 0.4.0", + "number_prefix", "portable-atomic", "rayon", - "unicode-width 0.2.0", + "unicode-width 0.2.1", "web-time", ] @@ -5911,7 +6098,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", ] [[package]] @@ -5928,7 +6115,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5990,6 +6177,17 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if 1.0.1", + "libc", +] + [[package]] name = "ioctl-rs" version = "0.1.6" @@ -6048,7 +6246,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.5.1", + "hermit-abi 0.5.2", "libc", "windows-sys 0.59.0", ] @@ -6068,15 +6266,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -6116,14 +6305,14 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49eedcbbb31f4e9f46566c543c0fb00b8f87ff9d88ba1a011aa262a5a1a2a505" dependencies = [ - "bitstream-io 4.1.0", + "bitstream-io 4.3.0", ] [[package]] name = "jiff" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -6138,13 +6327,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6169,7 +6358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "combine", "jni-sys", "log", @@ -6196,9 +6385,9 @@ dependencies = [ [[package]] name = "jpeg-decoder" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "js-sys" @@ -6410,15 +6599,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libfuzzer-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", @@ -6432,7 +6621,9 @@ checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" dependencies = [ "cc", "libc", + "libssh2-sys", "libz-sys", + "openssl-sys", "pkg-config", ] @@ -6442,7 +6633,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "winapi 0.3.9", ] @@ -6452,8 +6643,8 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ - "cfg-if 1.0.0", - "windows-targets 0.53.0", + "cfg-if 1.0.1", + "windows-targets 0.53.2", ] [[package]] @@ -6464,13 +6655,27 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.13", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", ] [[package]] @@ -6531,14 +6736,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] -name = "llguidance" -version = "0.7.26" +name = "little_exif" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8c0a03f43d553de79de69576bf29aeaf5792939514cf53d561c3e9b210820e" +checksum = "c9e2f6c60cdfde04b342f3136456b90bb7db46b93b0a76ed741c830a17c0c404" +dependencies = [ + "brotli 8.0.1", + "crc", + "log", + "miniz_oxide", + "paste", + "quick-xml 0.37.5", +] + +[[package]] +name = "llguidance" +version = "1.0.0" +source = "git+https://github.com/guidance-ai/llguidance.git?rev=c432092#c432092d37b8ccd1afeeff3e7f9c9a29aae0a87e" dependencies = [ "anyhow", "derivre", - "indexmap 2.9.0", + "indexmap 2.10.0", "regex-syntax 0.8.5", "serde", "serde_json", @@ -6593,7 +6811,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "generator", "scoped-tls", "tracing", @@ -6619,7 +6837,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -6630,11 +6848,11 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lz4_flex" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" dependencies = [ - "twox-hash", + "twox-hash 2.1.1", ] [[package]] @@ -6645,9 +6863,9 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] @@ -6709,9 +6927,9 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a8096766c229e8c88a3900c9b44b7e06aa7f7343cc229158c3e58ef8f9973a" +checksum = "2e4cd8c02f18a011991a039855480c64d74291c5792fcc160d55d77dc4de4a39" dependencies = [ "log", "tendril", @@ -6726,7 +6944,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6760,7 +6978,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "rayon", ] @@ -6772,9 +6990,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" @@ -6882,7 +7100,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6915,9 +7133,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.10.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd72e8b4e42274540edabec853f607c015c73436159b06c39c7af85a20433155" +checksum = "4e60ac08614cc09062820e51d5d94c2fce16b94ea4e5003bb81b99a95f84e876" dependencies = [ "serde", "serde_json", @@ -6925,9 +7143,9 @@ dependencies = [ [[package]] name = "minijinja-contrib" -version = "2.10.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457f85f9c4c5b17d11fcf9bbe7c0dbba64843c5ee040005956f1a510b6679fe2" +checksum = "f93e5bfa889f16d8c10ec92ac964074a68a7206c0fd9748ff23a31942c85d97c" dependencies = [ "minijinja", "serde", @@ -6941,9 +7159,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -6976,7 +7194,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -6987,7 +7205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -7018,37 +7236,49 @@ dependencies = [ [[package]] name = "mistralrs" version = "0.6.0" -source = "git+https://github.com/EricLBuehler/mistral.rs.git#39673eb8fa747235134e10367cdef90e2edafdd8" +source = "git+https://github.com/EricLBuehler/mistral.rs.git#8a4faf312069cf87b215c6c79e48a44e17b71062" dependencies = [ "anyhow", "candle-core", "candle-nn", - "clap 4.5.39", + "clap 4.5.40", "either", "futures", "image", - "indexmap 2.9.0", + "indexmap 2.10.0", "mistralrs-core", "rand 0.9.1", - "reqwest 0.12.19", + "reqwest", "serde", "serde_json", "tokio", "walkdir", ] +[[package]] +name = "mistralrs-audio" +version = "0.6.0" +source = "git+https://github.com/EricLBuehler/mistral.rs.git#8a4faf312069cf87b215c6c79e48a44e17b71062" +dependencies = [ + "anyhow", + "apodize", + "hound", + "symphonia", +] + [[package]] name = "mistralrs-core" version = "0.6.0" -source = "git+https://github.com/EricLBuehler/mistral.rs.git#39673eb8fa747235134e10367cdef90e2edafdd8" +source = "git+https://github.com/EricLBuehler/mistral.rs.git#8a4faf312069cf87b215c6c79e48a44e17b71062" dependencies = [ "ahash", "akin", "anyhow", + "apodize", "as-any", "async-trait", "base64 0.22.1", - "bindgen_cuda 0.1.5", + "bindgen_cuda 0.1.7", "bm25", "bytemuck", "bytemuck_derive", @@ -7057,30 +7287,34 @@ dependencies = [ "candle-nn", "cfgrammar", "chrono", - "clap 4.5.39", + "clap 4.5.40", "csv", "derive-new", - "derive_more", - "dirs 5.0.1", + "derive_more 2.0.1", + "dirs 6.0.0", "either", "float8", "futures", "galil-seiferas", "half", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "hf-hub", + "hound", "html2text", + "http 1.3.1", "image", - "indexmap 2.9.0", - "indicatif 0.17.11", + "indexmap 2.10.0", + "indicatif", "interprocess", - "itertools 0.13.0", + "itertools 0.14.0", "libc", "llguidance", "lrtable", "metal 0.27.0", "minijinja", "minijinja-contrib", + "mistralrs-audio", + "mistralrs-mcp", "mistralrs-paged-attn", "mistralrs-quant", "mistralrs-vision", @@ -7095,22 +7329,27 @@ dependencies = [ "rayon", "regex", "regex-automata 0.4.9", - "reqwest 0.12.19", + "reqwest", + "rubato", + "rust-mcp-schema", "rustc-hash 2.1.1", + "rustfft", "safetensors", - "schemars", + "schemars 0.8.22", "scraper", "serde", "serde-big-array", "serde_json", "serde_plain", "serde_yaml 0.9.34+deprecated", - "strum 0.26.3", + "strum 0.27.1", + "symphonia", "sysinfo 0.30.13", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokenizers", "tokio", "tokio-rayon", + "tokio-tungstenite", "toktrie_hf_tokenizers", "toml", "tqdm", @@ -7122,10 +7361,30 @@ dependencies = [ "vob", ] +[[package]] +name = "mistralrs-mcp" +version = "0.6.0" +source = "git+https://github.com/EricLBuehler/mistral.rs.git#8a4faf312069cf87b215c6c79e48a44e17b71062" +dependencies = [ + "anyhow", + "async-trait", + "futures-util", + "http 1.3.1", + "reqwest", + "rust-mcp-schema", + "serde", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "utoipa", + "uuid 1.17.0", +] + [[package]] name = "mistralrs-paged-attn" version = "0.6.0" -source = "git+https://github.com/EricLBuehler/mistral.rs.git#39673eb8fa747235134e10367cdef90e2edafdd8" +source = "git+https://github.com/EricLBuehler/mistral.rs.git#8a4faf312069cf87b215c6c79e48a44e17b71062" dependencies = [ "anyhow", "bindgen_cuda 0.1.7", @@ -7134,15 +7393,15 @@ dependencies = [ "half", "metal 0.27.0", "once_cell", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] name = "mistralrs-quant" version = "0.6.0" -source = "git+https://github.com/EricLBuehler/mistral.rs.git#39673eb8fa747235134e10367cdef90e2edafdd8" +source = "git+https://github.com/EricLBuehler/mistral.rs.git#8a4faf312069cf87b215c6c79e48a44e17b71062" dependencies = [ - "bindgen_cuda 0.1.5", + "bindgen_cuda 0.1.7", "byteorder", "candle-core", "candle-nn", @@ -7159,7 +7418,7 @@ dependencies = [ "safetensors", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tracing", "yoke 0.7.5", @@ -7168,7 +7427,7 @@ dependencies = [ [[package]] name = "mistralrs-vision" version = "0.6.0" -source = "git+https://github.com/EricLBuehler/mistral.rs.git#39673eb8fa747235134e10367cdef90e2edafdd8" +source = "git+https://github.com/EricLBuehler/mistral.rs.git#8a4faf312069cf87b215c6c79e48a44e17b71062" dependencies = [ "candle-core", "image", @@ -7193,12 +7452,12 @@ checksum = "c402a4092d5e204f32c9e155431046831fa712637043c58cb73bc6bc6c9663b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "multiple-daemons-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -7209,14 +7468,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", @@ -7234,7 +7493,7 @@ dependencies = [ "cfg_aliases", "codespan-reporting 0.11.1", "hexf-parse", - "indexmap 2.9.0", + "indexmap 2.10.0", "log", "rustc-hash 1.1.0", "spirv", @@ -7455,7 +7714,7 @@ checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags 1.3.2", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "memoffset 0.6.5", ] @@ -7467,7 +7726,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "memoffset 0.7.1", "pin-utils", @@ -7480,7 +7739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.9.1", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "cfg_aliases", "libc", "memoffset 0.9.1", @@ -7493,7 +7752,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.9.1", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "cfg_aliases", "libc", "memoffset 0.9.1", @@ -7532,9 +7791,9 @@ dependencies = [ [[package]] name = "nonempty-collections" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f301452dbaf00f14ca0c8204e46cf0c9a96f53543ac72cefa9b4d91c19e0ac" +checksum = "e216d0e8cf9d54fa66e5780f6e1d5dc96d1c1b3c25aeba3b6758548bcbbd8b9d" dependencies = [ "serde", ] @@ -7675,7 +7934,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -7726,29 +7985,30 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.5.1", + "hermit-abi 0.5.2", "libc", ] [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -7760,41 +8020,12 @@ dependencies = [ "libc", ] -[[package]] -name = "number_prefix" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a" - [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" -[[package]] -name = "nvml-wrapper" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9bff0aa1d48904a1385ea2a8b97576fbdcbc9a3cfccd0d31fe978e1c4038c5" -dependencies = [ - "bitflags 2.9.1", - "libloading 0.8.8", - "nvml-wrapper-sys", - "static_assertions", - "thiserror 1.0.69", - "wrapcenum-derive", -] - -[[package]] -name = "nvml-wrapper-sys" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "698d45156f28781a4e79652b6ebe2eaa0589057d588d3aec1333f6466f13fcb5" -dependencies = [ - "libloading 0.8.8", -] - [[package]] name = "objc" version = "0.2.7" @@ -8144,6 +8375,28 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-src" +version = "300.5.1+3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.18.0" @@ -8178,7 +8431,7 @@ dependencies = [ "bytes", "http 1.3.1", "opentelemetry 0.29.1", - "reqwest 0.12.19", + "reqwest", "tracing", ] @@ -8212,7 +8465,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk 0.29.0", "prost", - "reqwest 0.12.19", + "reqwest", "thiserror 2.0.12", "tokio", "tonic", @@ -8242,12 +8495,11 @@ dependencies = [ [[package]] name = "opentelemetry-system-metrics" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a54d9d46d8a7380cd3e84840aa56681b55cf9f5c8fd65fe5f9c4b1e0d87a5d68" +checksum = "3ff095ac36df870a11380877fb7e9b1e7529abfe994fd06a6d6d17ca1c77d30b" dependencies = [ "eyre", - "nvml-wrapper", "opentelemetry 0.29.1", "sysinfo 0.34.2", "tokio", @@ -8435,9 +8687,9 @@ version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.13", "smallvec", "windows-targets 0.52.6", ] @@ -8457,13 +8709,13 @@ dependencies = [ "arrow-schema", "arrow-select", "base64 0.22.1", - "brotli", + "brotli 7.0.0", "bytes", "chrono", "flate2", "futures", "half", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "lz4_flex", "num", "num-bigint", @@ -8473,7 +8725,7 @@ dependencies = [ "snap", "thrift 0.17.0", "tokio", - "twox-hash", + "twox-hash 1.6.3", "zstd", ] @@ -8551,9 +8803,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror 2.0.12", @@ -8562,9 +8814,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -8572,24 +8824,23 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -8601,7 +8852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.9.0", + "indexmap 2.10.0", ] [[package]] @@ -8644,7 +8895,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "unicase", ] @@ -8681,7 +8932,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -8758,15 +9009,15 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.1" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", - "indexmap 2.9.0", - "quick-xml 0.32.0", + "indexmap 2.10.0", + "quick-xml 0.38.0", "serde", - "time 0.3.41", + "time", ] [[package]] @@ -8872,7 +9123,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -8994,7 +9245,7 @@ checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "concurrent-queue", "libc", "log", @@ -9008,9 +9259,9 @@ version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "concurrent-queue", - "hermit-abi 0.5.1", + "hermit-abi 0.5.2", "pin-project-lite", "rustix 1.0.7", "tracing", @@ -9025,9 +9276,9 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -9059,7 +9310,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.25", + "zerocopy 0.8.26", ] [[package]] @@ -9080,12 +9331,21 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.33" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" dependencies = [ "proc-macro2", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", ] [[package]] @@ -9132,9 +9392,9 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", "puffin", @@ -9142,12 +9402,12 @@ dependencies = [ [[package]] name = "profiling-procmacros" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -9170,7 +9430,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -9191,7 +9451,7 @@ dependencies = [ "anyhow", "bincode", "byteorder", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "itertools 0.10.5", "lz4_flex", "once_cell", @@ -9252,7 +9512,7 @@ version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "eyre", "indoc", "inventory", @@ -9297,7 +9557,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -9310,7 +9570,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -9347,15 +9607,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" -[[package]] -name = "quick-xml" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26aab6b48e2590e4a64d1ed808749ba06257882b461d01ca71baeb747074a6dd" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.30.0" @@ -9366,15 +9617,6 @@ dependencies = [ "serde", ] -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.36.2" @@ -9395,7 +9637,16 @@ dependencies = [ ] [[package]] -name = "quinn" +name = "quick-xml" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" @@ -9422,9 +9673,9 @@ dependencies = [ "cfg_aliases", "pin-project-lite", "quinn-proto 0.11.12", - "quinn-udp 0.5.12", + "quinn-udp 0.5.13", "rustc-hash 2.1.1", - "rustls 0.23.27", + "rustls 0.23.28", "socket2 0.5.10", "thiserror 2.0.12", "tokio", @@ -9464,7 +9715,7 @@ dependencies = [ "rand 0.9.1", "ring 0.17.14", "rustc-hash 2.1.1", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pki-types", "rustls-platform-verifier", "slab", @@ -9489,9 +9740,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases", "libc", @@ -9512,9 +9763,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radix_trie" @@ -9618,8 +9869,8 @@ dependencies = [ "bitstream-io 2.6.0", "built", "cc", - "cfg-if 1.0.0", - "clap 4.5.39", + "cfg-if 1.0.1", + "clap 4.5.40", "clap_complete", "console", "fern", @@ -9689,7 +9940,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f067b45fa17e31d15636789c2638bd562da5496d498876cf0495df78f7e4fdcb" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "nix 0.23.2", "rand 0.8.5", @@ -9714,12 +9965,12 @@ dependencies = [ [[package]] name = "rayon-cond" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "059f538b55efd2309c9794130bc149c6a553db90e9d99c2030785c82f0bd7df9" +checksum = "2964d0cf57a3e7a06e8183d14a8b527195c706b7983549cd5462d5aa3747438f" dependencies = [ "either", - "itertools 0.11.0", + "itertools 0.14.0", "rayon", ] @@ -9741,9 +9992,9 @@ checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" [[package]] name = "re_analytics" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee5c4ddfcfbacba9eef6e9dd14a38831fad0827712336c892404a89a9655bb2" +checksum = "82c9df290dc9407f9f7b53b9c92c7373d641dbcdba76c0cd49c5360708f911a0" dependencies = [ "crossbeam", "directories", @@ -9755,7 +10006,7 @@ dependencies = [ "serde_json", "sha2", "thiserror 1.0.69", - "time 0.3.41", + "time", "url", "uuid 1.17.0", "web-sys", @@ -9763,9 +10014,9 @@ dependencies = [ [[package]] name = "re_arrow_util" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3b5e4226fbcc9d3dc3f0f188192b24c83c4ba837d55d512a5586c24f485110" +checksum = "15606a6a9e8e5eed86a209a5413e3368e21d79eca98b5b9af85dbe7cd59e0d0f" dependencies = [ "anyhow", "arrow", @@ -9777,9 +10028,9 @@ dependencies = [ [[package]] name = "re_blueprint_tree" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908471894526795dc2b65f14abb0ce9dd18f3923597957f53589b22a3f1e514f" +checksum = "6b39ebb6a7b7316106129f05fb8d16c6231fb6a19d2a221c59a2cee4e4161c88" dependencies = [ "egui", "egui_tiles", @@ -9799,9 +10050,9 @@ dependencies = [ [[package]] name = "re_build_info" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955f528061ad74bf04b31bcce654757274f21d600061e527f07738431d5c3581" +checksum = "d24cad542ae2adee88ee67b568a5a04b9e7939b0e37fa0efdab6f62d5d54a89e" dependencies = [ "re_byte_size", "serde", @@ -9809,24 +10060,24 @@ dependencies = [ [[package]] name = "re_build_tools" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ba8282c4bb4b0ccb0c7a6abd8862ddd527ed683f19fb4e6eea9f395e79d5ae5" +checksum = "4d00e7d749a13748aa091d4d672cea2e0436bbb8ecc7676b4aae7bb0f6999899" dependencies = [ "anyhow", "cargo_metadata 0.18.1", "glob", "sha2", - "time 0.3.41", + "time", "unindent", "walkdir", ] [[package]] name = "re_byte_size" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed1923c3b682f028c44a8ca6c88d414437557c32370ad988b43662e5cc062b7" +checksum = "54cfb83e7a9119dc90189a1e487d745e7bbd02f01e9c05eacf3dd07af40bce90" dependencies = [ "arrow", "half", @@ -9835,9 +10086,9 @@ dependencies = [ [[package]] name = "re_capabilities" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "737912ab2c6c9c41592897bd5fb824168187165e47dd2ea6442298dd9268576b" +checksum = "b9ed384fd6e96c9f6a2b355b88cc76a2f17e01b97c490ae5d39c1c8ebdc145e6" dependencies = [ "document-features", "egui", @@ -9846,18 +10097,18 @@ dependencies = [ [[package]] name = "re_case" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0cce186670cde73e385b3592145e5bc7a0cde5ccf7ba76d6bf52685afb32c" +checksum = "e325a73e920e202e9ee565e2fb4f128e786ae61fb17ffe91f12f23f341f479af" dependencies = [ "convert_case", ] [[package]] name = "re_chunk" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f738e73340c5c5cc3d6f0c6c3cbf25391219435501c68bf1d8a6a29f6a288db4" +checksum = "17a53fd0ae29c2739184b461977ab0beb6b06a4a0c14492b3d309c53f902df02" dependencies = [ "ahash", "anyhow", @@ -9886,9 +10137,9 @@ dependencies = [ [[package]] name = "re_chunk_store" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12a6d2bfdaf97f8fd544d129a302a759868edce9d15de58ea8e0b20ff7d14883" +checksum = "0cdeffc34474224753d3d7e7c3581fcc2629cde28618a91713c59d92c5ce0964" dependencies = [ "ahash", "anyhow", @@ -9917,9 +10168,9 @@ dependencies = [ [[package]] name = "re_chunk_store_ui" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cacd67ac9478d8b56d9b88338f2d2ee624f82197269bceca90d8f012327af8e0" +checksum = "d91e7f727112cb4dbb26c4028dddc2ea63e07c6733393701f4f5c94a9111e572" dependencies = [ "arrow", "egui", @@ -9936,9 +10187,9 @@ dependencies = [ [[package]] name = "re_component_ui" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc071389ecb22aa4f4c4119906d83b99fb3a782d4f489ea9eccaf4ed7befa575" +checksum = "1968d139a403aa987b89c6a88b4549172ac77bbb6490292927a7af5e8230f145" dependencies = [ "arrow", "egui", @@ -9956,9 +10207,9 @@ dependencies = [ [[package]] name = "re_context_menu" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19298d281887a698e4d9923e840bd6cd3ff8e445abb3e2e733bdba8c039c3ba" +checksum = "355757eb143c6656eabe1beb0f074a2934cf6a4a1a9b784bcfbcb7723f31cf90" dependencies = [ "egui", "egui_tiles", @@ -9980,9 +10231,9 @@ dependencies = [ [[package]] name = "re_crash_handler" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a745bfcaacc73685d3985c5469e49e82ffa9e7cbf289720f51f2305d6d048ec1" +checksum = "5bfc99a4efd49421b6cdfdd757816d795464368878da187b680ba8778f270a81" dependencies = [ "backtrace", "econtext", @@ -9995,9 +10246,9 @@ dependencies = [ [[package]] name = "re_data_loader" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e60ef0c0c7cc6355ed204226a3c9cb3da44ce88167e5b12f54e3c91f2240a64e" +checksum = "0e15f884392c4493fada8758d256f765e56b003cb2d9e317fa4d6514c38c71f2" dependencies = [ "ahash", "anyhow", @@ -10029,9 +10280,9 @@ dependencies = [ [[package]] name = "re_data_source" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55bafff46f885634a342cb792557f72ea74267eb177c563f72f9d78804872e8d" +checksum = "9ecb34ca8d063a7655c2d9e631ea918f03fe1f92d12b1528c89ae0ecabb591fc" dependencies = [ "anyhow", "itertools 0.14.0", @@ -10052,9 +10303,9 @@ dependencies = [ [[package]] name = "re_data_ui" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0290a67206dd3e02a5503be917a9a5ad60c37ed0619422d3f99278619c62a0bd" +checksum = "5af18c621ff1abc54027f735510f8ff17d163ca49c69bb5c70b7bc388abc38ad" dependencies = [ "ahash", "anyhow", @@ -10085,9 +10336,9 @@ dependencies = [ [[package]] name = "re_dataframe" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5303d8bd7f8770342e117e52ddd47b4c007215d932377a546dcef832cf7102f7" +checksum = "efbe0355d43334b430763efc50161f2533fbb089022cd0aff562a872fe5299da" dependencies = [ "anyhow", "arrow", @@ -10108,9 +10359,9 @@ dependencies = [ [[package]] name = "re_dataframe_ui" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f61b4ed6406f799bc737629bbb427f6bca2b55d296b42a16b7492d5868416cb" +checksum = "b4c409f2d4ab56d5c655ec964c3876d21235ef0717fdf4492f8239925ecf4d91" dependencies = [ "ahash", "arrow", @@ -10132,9 +10383,9 @@ dependencies = [ [[package]] name = "re_entity_db" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d5a4861c50b1d920b17fa5d57340a5ddea6e08284392c975fa607df8e39e1c" +checksum = "af21351a851096e7aae834ee5427011a7f0c3c7afbe87baeff2e996249c8c2c9" dependencies = [ "ahash", "document-features", @@ -10162,24 +10413,24 @@ dependencies = [ [[package]] name = "re_error" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd80ae3991ad4e631f7d6dfa1e01500812f8f25875d5f2620759626a6c3eda4f" +checksum = "a35e64f157ac0e69172d7dc6bf28e3a7cf9d7677ab2f1ea874995e9f6610a2e4" [[package]] name = "re_format" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bada3b35b0f5228a1c87573aebd34743c08e9f129053478524ecac02ccb2e37" +checksum = "2911aed8ef5b658871fc109072e056d77e8a8af9c438f9662ed5c88ac6850313" dependencies = [ "num-traits", ] [[package]] name = "re_format_arrow" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eac798346bb79095b69d8c14ad25ef90605fa18f7f027f30f97dd07a94ce77b" +checksum = "b721e96b17acdc524dc32bca09bad090014f1dbd3f4b3ae77f2a7154e1217582" dependencies = [ "arrow", "comfy-table", @@ -10192,9 +10443,9 @@ dependencies = [ [[package]] name = "re_grpc_client" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04ba37321ad65f9a9929117bab47e0a4b25c99c9e25234869b3df6c9000b9735" +checksum = "7376c4c1d8d1475f80a9b9f530beec818f0809469566c8093ac7483cfcdb92db" dependencies = [ "async-stream", "re_chunk", @@ -10217,9 +10468,9 @@ dependencies = [ [[package]] name = "re_grpc_server" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b707b51107674367b63fed53556a7b6ea3039ea586d5c0b5e4d2f388f0d135d1" +checksum = "262c5c3ea41d6f71a1869cbbbd71e7df9ce0b0c7cd4f0522c6bc343ce3f278b1" dependencies = [ "anyhow", "crossbeam", @@ -10247,9 +10498,9 @@ dependencies = [ [[package]] name = "re_int_histogram" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece02a2e674e50d2b78b9ee73dbde927897c8208ad84a703bf61c429d6b53bb8" +checksum = "cf786ea0d567180beae303294f2bb1551581f92446d012a65532c93219c63556" dependencies = [ "smallvec", "static_assertions", @@ -10257,9 +10508,9 @@ dependencies = [ [[package]] name = "re_log" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b664b9d1451d9814afeaf9c560af205962462ceb48ab068490b81fbea8d3cde2" +checksum = "1179e5409f57aebeedcea10d5cc4bea2bf5a9f237901501cb9a0e505ae06112f" dependencies = [ "env_filter", "env_logger 0.11.8", @@ -10273,9 +10524,9 @@ dependencies = [ [[package]] name = "re_log_encoding" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2544435ba46ac19158627193e4766c0644463ead1e9b9a3eef07fadedc7cab7" +checksum = "2bb060b8d19b399c320c311b43ab470bf3ad0994bf54967db8c886b13e24194a" dependencies = [ "arrow", "bytes", @@ -10304,9 +10555,9 @@ dependencies = [ [[package]] name = "re_log_types" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc03c54d712d2152be0d98822aa84c1cb5f8885ae2a0ddb8ec92bd78d290bbea" +checksum = "b59b776a823ef0f9897a1d37ed4d456aa1df9f789ed249936c1c6c3c774a91d2" dependencies = [ "ahash", "arrow", @@ -10351,9 +10602,9 @@ dependencies = [ [[package]] name = "re_memory" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ee4a108c2cb4ec3224fb68c4d32c7d8e6365f51739fb96223327f18ee193ac" +checksum = "9ee6d1efd5f0478116ca816ee1689995c7878107fe0ee06b7aecee8cd454ce00" dependencies = [ "ahash", "backtrace", @@ -10388,9 +10639,9 @@ dependencies = [ [[package]] name = "re_protos" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76fb1465af7370c02efd660fb2d84b1dea63eb13874c4f5ee18c3b482217a6f9" +checksum = "73696e35d1b46f7873ced423a8b2056f6659624cf33f555a88a53f13983460a2" dependencies = [ "arrow", "jiff", @@ -10410,9 +10661,9 @@ dependencies = [ [[package]] name = "re_query" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e5dcbcb0c14bdb0fcd188f3701e5d2ac2bb9574d2d04dc6568a0436a2102173" +checksum = "71558fbec0b921fdb3ade299e0def1dcc564b4f88c1585f8fae710102669ccf0" dependencies = [ "ahash", "anyhow", @@ -10448,7 +10699,7 @@ dependencies = [ "av-data", "bitflags 2.9.1", "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "nasm-rs 0.3.0", "parking_lot", @@ -10462,9 +10713,9 @@ dependencies = [ [[package]] name = "re_redap_browser" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b77a1c5aad96b817908e66454ed6eabd40b5d36f04d5fe14188920382aef12" +checksum = "90c4c6656351d9a2d8165b54064085cd331fc5fafcbdfdd893470839c6974acc" dependencies = [ "ahash", "arrow", @@ -10496,15 +10747,15 @@ dependencies = [ [[package]] name = "re_renderer" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619a6e59823932ae42c28331dc0290c0f6e02ca11fbcfe0c9ee0ed9451284681" +checksum = "424c62c5e555f78e05f60979e20d45dc65647a896ea56b130bb8db5f6f482787" dependencies = [ "ahash", "anyhow", "bitflags 2.9.1", "bytemuck", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "cfg_aliases", "clean-path", "crossbeam", @@ -10547,9 +10798,9 @@ dependencies = [ [[package]] name = "re_sdk" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f3a55c1b18e664c720d12abfd5dd3c2b09255b0595917447d78f9e0224fb88" +checksum = "c1d67360db529e0581c15c0ed0d028d8c531ec0f9fe0bebe6e2c9441bfb4daa8" dependencies = [ "ahash", "const_format", @@ -10578,14 +10829,14 @@ dependencies = [ "re_web_viewer_server", "thiserror 1.0.69", "tokio", - "webbrowser 1.0.4", + "webbrowser 1.0.5", ] [[package]] name = "re_selection_panel" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c58123c7c766c5d8f95ec8907b94252229de90be781983122faec0cb50f0cac" +checksum = "465dd77a7b4bfd12b50c20328090c5fbc15a36369ca34dd1617feba29cc94e40" dependencies = [ "arrow", "egui", @@ -10615,9 +10866,9 @@ dependencies = [ [[package]] name = "re_smart_channel" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca72cf9f303434055c0da7c6199e0af6ed7f22b92f2a0e92ae101d5c56784f1f" +checksum = "5ac0bb58c56bd6da543e820f6872e4310a76b55c797b3a182ae4cff9c86d9c72" dependencies = [ "crossbeam", "parking_lot", @@ -10629,9 +10880,9 @@ dependencies = [ [[package]] name = "re_sorbet" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58b8e4c2083e24067143f274b75b8ea5003fc8704b73de9dfdba20ca518479e" +checksum = "5fed7784e16e649baa4a50afeaf0353f37f1acf1a03e371cf62761947569c969" dependencies = [ "arrow", "itertools 0.14.0", @@ -10648,9 +10899,9 @@ dependencies = [ [[package]] name = "re_string_interner" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe2bf9b68c4268bbb71ad96f12e021c304221b604963fc2aba961f9ab4327622" +checksum = "0ccadaeab772cdb6731d00b1697558b55c3634e8773c4e5f69b17203e4b7a3cf" dependencies = [ "ahash", "nohash-hasher", @@ -10662,9 +10913,9 @@ dependencies = [ [[package]] name = "re_time_panel" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7e53e0a768687479ab1225cb203c849996c0d91f8989de7f43fd2eb28514ad" +checksum = "e8d29340e23a19b9ff421d1a593486fa346838239af30b48975c7a45c7c33a14" dependencies = [ "egui", "itertools 0.14.0", @@ -10691,9 +10942,9 @@ dependencies = [ [[package]] name = "re_tracing" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a2748417f58338c55e33dc145a76271251d31b1d1b3c8e1a5a5320fb4d1db3c" +checksum = "41f4ddc0564f0ae8fbe75a5ade3c66c9ddecf51bfcdf6aa4fa8b9ccce695a8fc" dependencies = [ "puffin", "puffin_http", @@ -10704,9 +10955,9 @@ dependencies = [ [[package]] name = "re_tuid" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9485126a443246010ff3ddb146b19ef2a181bdcf0b7d53d2b9c4145bb1c4009a" +checksum = "0a0726c9acdde34095bc612481404daf94e2a85288d94e41afb30414a5fe6206" dependencies = [ "bytemuck", "document-features", @@ -10719,9 +10970,9 @@ dependencies = [ [[package]] name = "re_types" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c7de8c28c83c0448c0c0a9d5ab43180b0180741bbaeec2495d2257b637e417" +checksum = "1d3c4a5882dded2b5080673d5ab0f6c8d5fa7a259b8c93f260b7381e04bb28b0" dependencies = [ "anyhow", "array-init", @@ -10762,9 +11013,9 @@ dependencies = [ [[package]] name = "re_types_builder" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b48b03120d689ff6d6a112f04216e3ff4ed437667cea76b824cb13822914451" +checksum = "93d8fdb651c8499468230fcf41fc9caf860ec7e2ee791d298cff0067031f30ad" dependencies = [ "anyhow", "camino", @@ -10773,7 +11024,7 @@ dependencies = [ "flatbuffers", "indent", "itertools 0.14.0", - "prettyplease 0.2.33", + "prettyplease 0.2.35", "proc-macro2", "quote", "rayon", @@ -10784,7 +11035,7 @@ dependencies = [ "re_tracing", "rust-format", "serde", - "syn 2.0.101", + "syn 2.0.104", "tempfile", "toml", "unindent", @@ -10793,9 +11044,9 @@ dependencies = [ [[package]] name = "re_types_core" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f48a3f00a5a503cabc7698e296c95f0ea8856083ede6bd84c679d39d597ec0a" +checksum = "435359a1ec8531b0f0808c61b90928e2d64e67477d2765fd98978304bab03b6b" dependencies = [ "anyhow", "arrow", @@ -10820,9 +11071,9 @@ dependencies = [ [[package]] name = "re_ui" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09baa5e611391d7351a49d346b9b4bfdbf9188b4da1884b9a90c7e9e9931efc" +checksum = "71f6b4b151b0cca4631f55d313480a94573a554f94689a6bcd971f817e7f1875" dependencies = [ "ahash", "arrow", @@ -10847,15 +11098,15 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "sublime_fuzzy", - "time 0.3.41", + "time", "url", ] [[package]] name = "re_uri" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7069d4503181d2a3bd0691059c52e9312125f3e9235f5062459671074eb7b07" +checksum = "af7aba1428a37b33eaaf56c73cd997eaf9734d629abf73957febaf3babf15de7" dependencies = [ "re_log", "re_log_types", @@ -10867,9 +11118,9 @@ dependencies = [ [[package]] name = "re_video" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fca7e997c713ecf366d8ed3a51a397f1a8b29d8bd5d773d66799fc01fdef78e" +checksum = "4df552942695d1f59a7e4a9681d67a1b04876225b38fe6e5182815529de08b58" dependencies = [ "bit-vec", "cfg_aliases", @@ -10895,9 +11146,9 @@ dependencies = [ [[package]] name = "re_view" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f490ad3de3829ec2e0b40ab04a4f5f6fff469278e4b34f8c19765a1b3d2bfe11" +checksum = "78edcef81ccc6e04318a70d6674076b5324ebf2bbc00994f6f1ec0754f3b57cc" dependencies = [ "ahash", "arrow", @@ -10921,9 +11172,9 @@ dependencies = [ [[package]] name = "re_view_bar_chart" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61d4822050e2c8827409a7cb1a901cde4f19df4f0dd62699b500e62a2cee45be" +checksum = "3ea30060e7d10950ec0cbe18c55571e081fa6fc63e8c9074454c258d553c60cb" dependencies = [ "egui", "egui_plot", @@ -10941,9 +11192,9 @@ dependencies = [ [[package]] name = "re_view_dataframe" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58e50926d9a879ecbdcfd22247283c61bf2b6d9d9e1e6cb8ebc9660e81b6045" +checksum = "62e89ee0b320d257a77d32e8235ccdd5c3dfb871a892837b8c18c31f77547820" dependencies = [ "anyhow", "arrow", @@ -10969,9 +11220,9 @@ dependencies = [ [[package]] name = "re_view_graph" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26cb1dfd28e7a4725b2d3ea9996f7357d53e534d5d3fe71651c848c3d3251b6" +checksum = "8da0aab49e034353dacb2788afbba0c06aab7b5f4ba0367d995d623b3ebe8bd0" dependencies = [ "ahash", "egui", @@ -10996,9 +11247,9 @@ dependencies = [ [[package]] name = "re_view_map" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8538285c4b75628c093a60771f6d0cc1c01eec3ac82039b6e9c83e110f6774" +checksum = "8a561880e7b7b801a495c22e980aa4637d83951e6218dff1dabe062419d5400b" dependencies = [ "bytemuck", "egui", @@ -11022,9 +11273,9 @@ dependencies = [ [[package]] name = "re_view_spatial" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305425ed716c94f1dd4bbccbacee7c0fc118cdf8d1470ddf0f5d82370f1fa35b" +checksum = "642bc7cd5ca26c85dc26d88fdf16329f23c40082c782efa1f47888754d9e570b" dependencies = [ "ahash", "anyhow", @@ -11065,9 +11316,9 @@ dependencies = [ [[package]] name = "re_view_tensor" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4307d93518498313ec35dc7c085198bbb7b9a2f8b24b77db95208488294ab7" +checksum = "5ee06e88b8410b2fd2d42b7ed83713fa4777e9eb8dec10e7baa7e66cf123cdd2" dependencies = [ "anyhow", "bytemuck", @@ -11091,9 +11342,9 @@ dependencies = [ [[package]] name = "re_view_text_document" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d5aae4c9b41fc3d78bba572b58b1785f70605123371ae2a77fb94bd3438bb0c" +checksum = "93c372c65c4780907400dff6f3a6b5891c41d28a0475c64bfada796a274547c6" dependencies = [ "egui", "egui_commonmark", @@ -11108,9 +11359,9 @@ dependencies = [ [[package]] name = "re_view_text_log" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abb27014e05c48a74139f8be69222d704414e02a825df85371228cdcc88aa22" +checksum = "9a8829fc22595ca0c9c5a2dd6f78eccc799a950c360a3e877c63c7b51a2de177" dependencies = [ "egui", "egui_extras", @@ -11130,9 +11381,9 @@ dependencies = [ [[package]] name = "re_view_time_series" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d17c2cc4ea372c3cd6387a7dbca59a2776ad2c3d8193df08838ea95a916dff4" +checksum = "8b163578ed3cbccb0d7917f24e155c26e3b5fdbae7c2a2b07600471576264eb7" dependencies = [ "egui", "egui_plot", @@ -11156,15 +11407,15 @@ dependencies = [ [[package]] name = "re_viewer" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6f6a64121af0b17108b0711c2ed584b09055bf77b083b70a4f924c272f36f8" +checksum = "425234e1e4cca4272eaaa18189f27400cd0ef26f520e57b7eb44882111d13213" dependencies = [ "ahash", "anyhow", "arrow", "bytemuck", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "crossbeam", "eframe", "egui", @@ -11242,9 +11493,9 @@ dependencies = [ [[package]] name = "re_viewer_context" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff905b6a6b98604dbece3fde70f85c9dbe51cd25e118e620a5927e3de1e0878" +checksum = "365710a963ed2257d095467c8fd3e5c52f1245af0545725a80fcee761dc70a93" dependencies = [ "ahash", "anyhow", @@ -11262,7 +11513,7 @@ dependencies = [ "half", "home", "image", - "indexmap 2.9.0", + "indexmap 2.10.0", "itertools 0.14.0", "linked-hash-map", "ndarray 0.16.1", @@ -11306,9 +11557,9 @@ dependencies = [ [[package]] name = "re_viewport" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3277beb27b91bee93c3c78e4a64a128be7025652f6ca3807f109c7c9d9fe3f9" +checksum = "c91afdd7c3f0af2794572f0e5e92c4275280c99e3d51a74e206aa6a9816befae" dependencies = [ "ahash", "egui", @@ -11330,9 +11581,9 @@ dependencies = [ [[package]] name = "re_viewport_blueprint" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c497854092f093262a652d8cfab7c497fedfa885de83ac9894d04570c1bdc54" +checksum = "b16b7d402650145454cd2a95fe305f38a87c7f066a51fddff4aa6bcd239631b3" dependencies = [ "ahash", "arrow", @@ -11359,9 +11610,9 @@ dependencies = [ [[package]] name = "re_web_viewer_server" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55cc4b7025804aee193619e6ee80422e32a8a9d393fbafb06d072f1f9a19be3" +checksum = "640c8b844389726f067c4e38674f038ae396fb7baa2940ef411c60ffc27002c6" dependencies = [ "document-features", "re_analytics", @@ -11370,6 +11621,15 @@ dependencies = [ "tiny_http", ] +[[package]] +name = "realfft" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f821338fddb99d089116342c46e9f1fbf3828dba077674613e734e01d6ea8677" +dependencies = [ + "rustfft", +] + [[package]] name = "reborrow" version = "0.5.5" @@ -11378,7 +11638,7 @@ checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" [[package]] name = "receive_data" -version = "0.3.11" +version = "0.3.12" dependencies = [ "chrono", "dora-node-api", @@ -11402,9 +11662,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] @@ -11448,7 +11708,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -11457,10 +11717,10 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c81d000a2c524133cc00d2f92f019d399e57906c3b7119271a2495354fe895" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "rustix 1.0.7", - "windows 0.61.1", + "windows 0.61.3", ] [[package]] @@ -11515,50 +11775,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", - "hyper-rustls 0.24.2", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", - "tokio", - "tokio-rustls 0.24.1", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.4", - "winreg", -] - -[[package]] -name = "reqwest" -version = "0.12.19" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64 0.22.1", "bytes", @@ -11566,29 +11785,27 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.10", + "h2 0.4.11", "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", - "hyper-rustls 0.27.6", + "hyper-rustls", "hyper-util", - "ipnet", "js-sys", "log", "mime", - "once_cell", "percent-encoding", "pin-project-lite", "quinn 0.11.8", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", "tokio-util", "tower 0.5.2", "tower-http 0.6.6", @@ -11598,7 +11815,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.0", + "webpki-roots 1.0.1", ] [[package]] @@ -11610,7 +11827,7 @@ dependencies = [ "anyhow", "async-trait", "http 1.3.1", - "reqwest 0.12.19", + "reqwest", "serde", "thiserror 1.0.69", "tower-service", @@ -11618,9 +11835,9 @@ dependencies = [ [[package]] name = "rerun" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be0e5426692333e0b436d0c92c8f95239432538bc644ed0cd2a9319ed22f094" +checksum = "4ef9c0199c82495f3c57175bfdf8b6ed3f22e4e0f5a20421ff0775eec91ea63f" dependencies = [ "anyhow", "arrow", @@ -11628,8 +11845,8 @@ dependencies = [ "crossbeam", "document-features", "env_filter", - "indexmap 2.9.0", - "indicatif 0.17.11", + "indexmap 2.10.0", + "indicatif", "itertools 0.14.0", "log", "puffin", @@ -11710,9 +11927,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "a457e416a0f90d246a4c3288bd7a25b2304ca727f253f95be383dd17af56be8f" dependencies = [ "bytemuck", ] @@ -11739,7 +11956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "getrandom 0.2.16", "libc", "untrusted 0.9.0", @@ -11796,12 +12013,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15b491a0c37fb42f0e6cdcee24b2aa427acb298658576073920f4b8529f7012" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "bstr", "bytes", "cdr-encoding-size", "chrono", - "clap 4.5.39", + "clap 4.5.40", "futures", "itertools 0.14.0", "lazy_static", @@ -11866,9 +12083,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rubato" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5258099699851cfd0082aeb645feb9c084d9a5e1f1b8d5372086b989fc5e56a1" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "realfft", +] + [[package]] name = "rust-dataflow-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11879,7 +12108,7 @@ dependencies = [ [[package]] name = "rust-dataflow-example-sink" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11887,7 +12116,7 @@ dependencies = [ [[package]] name = "rust-dataflow-example-sink-dynamic" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11895,7 +12124,7 @@ dependencies = [ [[package]] name = "rust-dataflow-example-status-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -11912,9 +12141,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rust-mcp-schema" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69c8c97bf79c576f8dc582be9f6c9825ed91bd921aac65bd7990992257727e39" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "rust-ros2-dataflow-example-node" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "dora-ros2-bridge", @@ -11938,9 +12177,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.37.1" +version = "1.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" +checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" dependencies = [ "arrayvec", "num-traits", @@ -11948,9 +12187,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -11970,7 +12209,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.26", + "semver", ] [[package]] @@ -12010,6 +12249,20 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "rustfft" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f140db74548f7c9d7cce60912c9ac414e74df5e718dc947d514b051b42f3f4" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + [[package]] name = "rusticata-macros" version = "4.1.0" @@ -12073,21 +12326,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring 0.17.14", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.23.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" dependencies = [ "log", "once_cell", @@ -12161,7 +12402,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-native-certs 0.8.1", "rustls-platform-verifier-android", "rustls-webpki 0.103.3", @@ -12177,16 +12418,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", -] - [[package]] name = "rustls-webpki" version = "0.102.8" @@ -12217,11 +12448,12 @@ checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rustypot" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cea06baa75ad1b4930d4e9de88953e44184a9485fb0f82b9a2a2ddb8ed5933" +checksum = "554ad4276591fd5858d16d639b799b5acdab2f2e4478d65016454454083a542e" dependencies = [ - "clap 4.5.39", + "clap 4.5.40", + "env_logger 0.10.2", "log", "num_enum", "paste", @@ -12324,6 +12556,30 @@ dependencies = [ "serde_json", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars_derive" version = "0.8.22" @@ -12333,7 +12589,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -12446,7 +12702,7 @@ checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" dependencies = [ "bitflags 2.9.1", "cssparser", - "derive_more", + "derive_more 0.99.20", "fxhash", "log", "new_debug_unreachable", @@ -12470,32 +12726,26 @@ dependencies = [ [[package]] name = "self_update" -version = "0.27.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb85f1802f7b987237b8525c0fde86ea86f31c957c1875467c727d5b921179c" +checksum = "d832c086ece0dacc29fb2947bb4219b8f6e12fe9e40b7108f9e57c4224e47b5c" dependencies = [ "either", "flate2", - "hyper 0.14.32", - "indicatif 0.15.0", + "hyper 1.6.0", + "indicatif", "log", - "quick-xml 0.20.0", + "quick-xml 0.37.5", "regex", - "reqwest 0.11.27", - "semver 0.11.0", + "reqwest", + "self-replace", + "semver", "serde_json", "tar", "tempfile", - "zip 0.5.13", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", + "urlencoding", + "zip 2.4.2", + "zipsign-api", ] [[package]] @@ -12507,15 +12757,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" -dependencies = [ - "pest", -] - [[package]] name = "seq-macro" version = "0.3.6" @@ -12599,7 +12840,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -12610,7 +12851,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -12619,7 +12860,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "memchr", "ryu", @@ -12643,14 +12884,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -12669,32 +12910,34 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", "serde_with_macros", - "time 0.3.41", + "time", ] [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -12715,7 +12958,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -12771,7 +13014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb0bc984f6af6ef8bab54e6cf2071579ee75b9286aa9f2319a0d220c28b0a2b" dependencies = [ "bitflags 2.9.1", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "core-foundation 0.10.1", "core-foundation-sys", "io-kit-sys", @@ -12784,9 +13027,9 @@ dependencies = [ [[package]] name = "servo_arc" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" +checksum = "204ea332803bd95a0b60388590d59cf6468ec9becf626e2451f1d26a1d972de4" dependencies = [ "stable_deref_trait", ] @@ -12797,7 +13040,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "cpufeatures", "digest", ] @@ -12808,7 +13051,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "cpufeatures", "digest", ] @@ -12819,7 +13062,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "cpufeatures", "digest", ] @@ -12851,7 +13094,7 @@ dependencies = [ [[package]] name = "shared-memory-server" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bincode", "eyre", @@ -12867,7 +13110,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004d7ece9a3be64f85471d50967710b0a146144225bed5f0abd0514a3bed086f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "libc", "nix 0.26.4", "rand 0.8.5", @@ -13058,12 +13301,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "slotmap" @@ -13077,9 +13317,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] @@ -13209,7 +13449,7 @@ checksum = "658f2ca5276b92c3dfd65fa88316b4e032ace68f88d7570b43967784c0bac5ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -13351,7 +13591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" dependencies = [ "async-channel 1.9.0", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "futures-core", "pin-project-lite", ] @@ -13365,6 +13605,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + [[package]] name = "strict-num" version = "0.1.1" @@ -13418,93 +13664,228 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "structopt" -version = "0.3.26" +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.34.0", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros 0.27.1", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.104", +] + +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.104", +] + +[[package]] +name = "sublime_fuzzy" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7986063f7c0ab374407e586d7048a3d5aac94f103f751088bf398e07cd5400" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svgtypes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" +dependencies = [ + "kurbo", + "siphasher 0.3.11", +] + +[[package]] +name = "symphonia" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9" +dependencies = [ + "lazy_static", + "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-pcm", + "symphonia-codec-vorbis", + "symphonia-core", + "symphonia-format-isomp4", + "symphonia-format-ogg", + "symphonia-format-riff", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-flac" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e34f34298a7308d4397a6c7fbf5b84c5d491231ce3dd379707ba673ab3bd97" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4" dependencies = [ - "clap 2.34.0", "lazy_static", - "structopt-derive", + "log", + "symphonia-core", + "symphonia-metadata", ] [[package]] -name = "structopt-derive" -version = "0.4.18" +name = "symphonia-codec-pcm" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +checksum = "f395a67057c2ebc5e84d7bb1be71cce1a7ba99f64e0f0f0e303a03f79116f89b" dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", + "log", + "symphonia-core", ] [[package]] -name = "strum" -version = "0.26.3" +name = "symphonia-codec-vorbis" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "5a98765fb46a0a6732b007f7e2870c2129b6f78d87db7987e6533c8f164a9f30" dependencies = [ - "strum_macros 0.26.4", + "log", + "symphonia-core", + "symphonia-utils-xiph", ] [[package]] -name = "strum" -version = "0.27.1" +name = "symphonia-core" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3" dependencies = [ - "strum_macros 0.27.1", + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", ] [[package]] -name = "strum_macros" -version = "0.26.4" +name = "symphonia-format-isomp4" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "abfdf178d697e50ce1e5d9b982ba1b94c47218e03ec35022d9f0e071a16dc844" dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.101", + "encoding_rs", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", ] [[package]] -name = "strum_macros" -version = "0.27.1" +name = "symphonia-format-ogg" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "ada3505789516bcf00fc1157c67729eded428b455c27ca370e41f4d785bfa931" dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.101", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", ] [[package]] -name = "sublime_fuzzy" -version = "0.7.0" +name = "symphonia-format-riff" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7986063f7c0ab374407e586d7048a3d5aac94f103f751088bf398e07cd5400" +checksum = "05f7be232f962f937f4b7115cbe62c330929345434c834359425e043bfd15f50" +dependencies = [ + "extended", + "log", + "symphonia-core", + "symphonia-metadata", +] [[package]] -name = "subtle" -version = "2.6.1" +name = "symphonia-metadata" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] [[package]] -name = "svgtypes" -version = "0.13.0" +name = "symphonia-utils-xiph" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" +checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe" dependencies = [ - "kurbo", - "siphasher 0.3.11", + "symphonia-core", + "symphonia-metadata", ] [[package]] @@ -13520,21 +13901,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -13552,7 +13927,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -13597,7 +13972,7 @@ version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "core-foundation-sys", "libc", "ntapi", @@ -13633,17 +14008,6 @@ dependencies = [ "windows 0.57.0", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "system-configuration-sys 0.5.0", -] - [[package]] name = "system-configuration" version = "0.6.1" @@ -13652,17 +14016,7 @@ checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.1", "core-foundation 0.9.4", - "system-configuration-sys 0.6.0", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", + "system-configuration-sys", ] [[package]] @@ -13694,7 +14048,7 @@ version = "7.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" dependencies = [ - "cfg-expr 0.20.0", + "cfg-expr 0.20.1", "heck 0.5.0", "pkg-config", "toml", @@ -13707,7 +14061,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce91f2f0ec87dff7e6bcbbeb267439aa1188703003c6055193c821487400432" dependencies = [ - "unicode-width 0.2.0", + "unicode-width 0.2.1", ] [[package]] @@ -13774,7 +14128,7 @@ dependencies = [ [[package]] name = "terminal-print" -version = "0.3.11" +version = "0.3.12" dependencies = [ "dora-node-api", "eyre", @@ -13840,7 +14194,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -13851,17 +14205,16 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if 1.0.0", - "once_cell", + "cfg-if 1.0.1", ] [[package]] @@ -13908,17 +14261,6 @@ dependencies = [ "weezl", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", -] - [[package]] name = "time" version = "0.3.41" @@ -13971,7 +14313,7 @@ dependencies = [ "arrayref", "arrayvec", "bytemuck", - "cfg-if 1.0.0", + "cfg-if 1.0.1", "log", "png", "tiny-skia-path", @@ -14044,7 +14386,7 @@ dependencies = [ "pin-project-lite", "thiserror 2.0.12", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", ] [[package]] @@ -14073,22 +14415,24 @@ dependencies = [ [[package]] name = "tokenizers" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3169b3195f925496c895caee7978a335d49218488ef22375267fba5a46a40bd7" +checksum = "4c3846d8588abed0daba25a0e47edd58ea15e450a6088b2575f5116fdb0b27ca" dependencies = [ + "ahash", "aho-corasick", + "compact_str", + "dary_heap", "derive_builder", "esaxx-rs", "fancy-regex", - "getrandom 0.2.16", - "itertools 0.13.0", - "lazy_static", + "getrandom 0.3.3", + "itertools 0.14.0", "log", "macro_rules_attribute 0.2.2", "monostate", "paste", - "rand 0.8.5", + "rand 0.9.1", "rayon", "rayon-cond", "regex", @@ -14104,17 +14448,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio 1.0.4", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2 0.5.10", "tokio-macros", "windows-sys 0.52.0", @@ -14128,7 +14474,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -14141,23 +14487,13 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.27", + "rustls 0.23.28", "tokio", ] @@ -14195,16 +14531,15 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "pin-project-lite", "tokio", ] [[package]] name = "toktrie" -version = "0.7.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a120eb54b99de14c0e2e0b26466902cba0030edf0015d1b4c47d76be3b3cba1a" +version = "1.0.0" +source = "git+https://github.com/guidance-ai/llguidance.git?rev=c432092#c432092d37b8ccd1afeeff3e7f9c9a29aae0a87e" dependencies = [ "anyhow", "bytemuck", @@ -14215,9 +14550,8 @@ dependencies = [ [[package]] name = "toktrie_hf_tokenizers" -version = "0.7.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605e17fab3e47ac7e489b51865979705956737c415fc9994a08cce58b659efce" +version = "1.0.0" +source = "git+https://github.com/guidance-ai/llguidance.git?rev=c432092#c432092d37b8ccd1afeeff3e7f9c9a29aae0a87e" dependencies = [ "anyhow", "log", @@ -14229,11 +14563,11 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -14242,20 +14576,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -14265,9 +14599,9 @@ dependencies = [ [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "tonic" @@ -14280,7 +14614,7 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.10", + "h2 0.4.11", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -14294,7 +14628,7 @@ dependencies = [ "rustls-pemfile 2.2.0", "socket2 0.5.10", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", "tokio-stream", "tower 0.4.13", "tower-layer", @@ -14376,7 +14710,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -14453,20 +14787,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -14539,6 +14873,16 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -14575,10 +14919,16 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "static_assertions", ] +[[package]] +name = "twox-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56" + [[package]] name = "type-map" version = "0.5.1" @@ -14690,9 +15040,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" @@ -14780,7 +15130,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pki-types", "serde", "serde_json", @@ -14869,6 +15219,29 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utoipa" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +dependencies = [ + "indexmap 2.10.0", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "uuid" version = "0.8.2" @@ -14900,16 +15273,16 @@ checksum = "26b682e8c381995ea03130e381928e0e005b7c9eb483c6c8682f50e07b33c2b7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "v_frame" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" dependencies = [ - "aligned-vec", + "aligned-vec 0.6.4", "num-traits", "serde", "wasm-bindgen", @@ -14935,7 +15308,7 @@ checksum = "dcba0282a9f9297af06b91ff22615e7f77f0ab66f75fc95898960d1604fc7fd7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "unzip-n", ] @@ -15000,9 +15373,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vob" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba59a857adc264b7783397cc7b4bb2aa02d7fff59fd89be54ae701af5f64eb5c" +checksum = "0baa046ba374a7701d98032a468a0bbd968a8cd3a2ae39c94d74e211fac05c81" dependencies = [ "num-traits", "serde", @@ -15038,7 +15411,7 @@ dependencies = [ "image", "log", "lru", - "reqwest 0.12.19", + "reqwest", "reqwest-middleware", "thiserror 2.0.12", "tokio", @@ -15056,15 +15429,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -15081,7 +15448,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -15097,7 +15464,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -15107,7 +15474,7 @@ version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.1", "js-sys", "once_cell", "wasm-bindgen", @@ -15132,7 +15499,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -15290,9 +15657,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9c5f0bc545ea3b20b423e33b9b457764de0b3730cd957f6c6aa6c301785f6e" +checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" dependencies = [ "phf", "phf_codegen", @@ -15319,12 +15686,11 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5df295f8451142f1856b1bd86a606dfe9587d439bc036e319c827700dbd555e" +checksum = "aaf4f3c0ba838e82b4e5ccc4157003fb8c324ee24c058470ffb82820becbde98" dependencies = [ "core-foundation 0.10.1", - "home", "jni", "log", "ndk-context", @@ -15350,14 +15716,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.0", + "webpki-root-certs 1.0.1", ] [[package]] name = "webpki-root-certs" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" +checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" dependencies = [ "rustls-pki-types", ] @@ -15371,26 +15737,20 @@ dependencies = [ "webpki", ] -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - [[package]] name = "webpki-roots" version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.0", + "webpki-roots 1.0.1", ] [[package]] name = "webpki-roots" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" dependencies = [ "rustls-pki-types", ] @@ -15438,7 +15798,7 @@ dependencies = [ "bitflags 2.9.1", "cfg_aliases", "document-features", - "indexmap 2.9.0", + "indexmap 2.10.0", "log", "naga", "once_cell", @@ -15521,9 +15881,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" dependencies = [ "bytemuck", "safe_arch", @@ -15641,9 +16001,9 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", "windows-core 0.61.2", @@ -15727,7 +16087,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -15738,7 +16098,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -15749,7 +16109,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -15760,7 +16120,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -15771,7 +16131,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -15782,14 +16142,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-numerics" @@ -15803,13 +16163,13 @@ dependencies = [ [[package]] name = "windows-registry" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ + "windows-link", "windows-result 0.3.4", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-strings 0.4.2", ] [[package]] @@ -15849,15 +16209,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -15918,6 +16269,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -15966,9 +16326,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -16253,23 +16613,13 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if 1.0.0", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -16299,18 +16649,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "wrapcenum-derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "writeable" version = "0.6.1" @@ -16373,14 +16711,14 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror 1.0.69", - "time 0.3.41", + "time", ] [[package]] name = "xattr" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" dependencies = [ "libc", "rustix 1.0.7", @@ -16388,9 +16726,9 @@ dependencies = [ [[package]] name = "xcursor" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xdg-home" @@ -16501,7 +16839,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -16513,7 +16851,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -16557,9 +16895,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.7.1" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +checksum = "597f45e98bc7e6f0988276012797855613cd8269e23b5be62cc4e5d28b7e515d" dependencies = [ "async-broadcast", "async-executor", @@ -16583,9 +16921,9 @@ dependencies = [ "uds_windows", "windows-sys 0.59.0", "winnow", - "zbus_macros 5.7.1", + "zbus_macros 5.8.0", "zbus_names 4.2.0", - "zvariant 5.5.3", + "zvariant 5.6.0", ] [[package]] @@ -16606,7 +16944,7 @@ checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zbus-lockstep", "zbus_xml", "zvariant 4.2.0", @@ -16621,22 +16959,22 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zvariant_utils 2.1.0", ] [[package]] name = "zbus_macros" -version = "5.7.1" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +checksum = "e5c8e4e14dcdd9d97a98b189cd1220f30e8394ad271e8c987da84f73693862c2" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zbus_names 4.2.0", - "zvariant 5.5.3", + "zvariant 5.6.0", "zvariant_utils 3.2.0", ] @@ -16660,7 +16998,7 @@ dependencies = [ "serde", "static_assertions", "winnow", - "zvariant 5.5.3", + "zvariant 5.6.0", ] [[package]] @@ -16944,7 +17282,7 @@ dependencies = [ "hashbrown 0.14.5", "keyed-set", "rand 0.8.5", - "schemars", + "schemars 0.8.22", "serde", "token-cell", "zenoh-result", @@ -17014,11 +17352,11 @@ dependencies = [ "async-trait", "flume 0.11.1", "futures", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-webpki 0.102.8", "serde", "socket2 0.5.10", - "time 0.3.41", + "time", "tokio", "tokio-util", "tracing", @@ -17064,12 +17402,12 @@ dependencies = [ "async-trait", "base64 0.22.1", "quinn 0.11.8", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pemfile 2.2.0", "rustls-pki-types", "rustls-webpki 0.102.8", "secrecy", - "time 0.3.41", + "time", "tokio", "tokio-util", "tracing", @@ -17148,16 +17486,16 @@ checksum = "dc7d003c89eb55b71c11dc832fd48d949c4f89c1c878a3164ff1ce4d35e2210a" dependencies = [ "async-trait", "base64 0.22.1", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pemfile 2.2.0", "rustls-pki-types", "rustls-webpki 0.102.8", "secrecy", "socket2 0.5.10", - "time 0.3.41", + "time", "tls-listener", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", "tokio-util", "tracing", "webpki-roots 0.26.11", @@ -17287,7 +17625,7 @@ checksum = "0f8223de12a20902379393be6949643fefc4d36b00bd05c8bc10d45974ee8d54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zenoh-keyexpr", ] @@ -17561,11 +17899,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ - "zerocopy-derive 0.8.25", + "zerocopy-derive 0.8.26", ] [[package]] @@ -17576,18 +17914,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -17607,7 +17945,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -17647,34 +17985,49 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "zip" -version = "0.5.13" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", + "crossbeam-utils", + "displaydoc", + "indexmap 2.10.0", + "num_enum", "thiserror 1.0.69", - "time 0.1.45", ] [[package]] name = "zip" -version = "1.1.4" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" dependencies = [ "arbitrary", "crc32fast", "crossbeam-utils", "displaydoc", - "indexmap 2.9.0", - "num_enum", - "thiserror 1.0.69", + "indexmap 2.10.0", + "memchr", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "zipsign-api" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba6063ff82cdbd9a765add16d369abe81e520f836054e997c2db217ceca40c0" +dependencies = [ + "base64 0.22.1", + "ed25519-dalek", + "thiserror 2.0.12", ] [[package]] @@ -17722,9 +18075,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.15" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63fd466826ec8fe25e1fc010c169213fec4e135ac39caccdba830eaa3895923" +checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" dependencies = [ "zune-core", ] @@ -17744,16 +18097,16 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" dependencies = [ "endi", "enumflags2", "serde", "url", "winnow", - "zvariant_derive 5.5.3", + "zvariant_derive 5.6.0", "zvariant_utils 3.2.0", ] @@ -17766,20 +18119,20 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zvariant_utils 2.1.0", ] [[package]] name = "zvariant_derive" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zvariant_utils 3.2.0", ] @@ -17791,7 +18144,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -17804,6 +18157,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.101", + "syn 2.0.104", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 93f68c33..9c433b1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,36 +50,37 @@ 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-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.4.4", 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" } @@ -91,6 +92,8 @@ pyo3 = { version = "0.23", features = [ "multiple-pymethods", ] } pythonize = "0.23" +git2 = { version = "0.18.0", features = ["vendored-openssl"] } +serde_yaml = "0.9.33" [package] name = "dora-examples" @@ -107,6 +110,7 @@ ros2-examples = [] [dev-dependencies] eyre = "0.6.8" tokio = "1.24.2" +dora-cli = { workspace = true } dora-coordinator = { workspace = true } dora-core = { workspace = true } dora-message = { workspace = true } @@ -135,6 +139,10 @@ path = "examples/vlm/run.rs" name = "rust-dataflow" path = "examples/rust-dataflow/run.rs" +[[example]] +name = "rust-dataflow-git" +path = "examples/rust-dataflow-git/run.rs" + [[example]] name = "rust-ros2-dataflow" path = "examples/rust-ros2-dataflow/run.rs" 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..a4dde388 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@
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, - \[03/25\] Add support for dora async Python. @@ -102,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) | @@ -134,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 @@ -144,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) | diff --git a/apis/c++/node/src/lib.rs b/apis/c++/node/src/lib.rs index a7ec233a..4e06e11a 100644 --- a/apis/c++/node/src/lib.rs +++ b/apis/c++/node/src/lib.rs @@ -144,7 +144,7 @@ pub struct DoraEvent(Option); fn event_type(event: &DoraEvent) -> ffi::DoraEventType { match &event.0 { Some(event) => match event { - Event::Stop => ffi::DoraEventType::Stop, + Event::Stop(_) => ffi::DoraEventType::Stop, Event::Input { .. } => ffi::DoraEventType::Input, Event::InputClosed { .. } => ffi::DoraEventType::InputClosed, Event::Error(_) => ffi::DoraEventType::Error, diff --git a/apis/c/node/src/lib.rs b/apis/c/node/src/lib.rs index 9d8762b0..8720c795 100644 --- a/apis/c/node/src/lib.rs +++ b/apis/c/node/src/lib.rs @@ -91,7 +91,7 @@ pub unsafe extern "C" fn dora_next_event(context: *mut c_void) -> *mut c_void { pub unsafe extern "C" fn read_dora_event_type(event: *const ()) -> EventType { let event: &Event = unsafe { &*event.cast() }; match event { - Event::Stop => EventType::Stop, + Event::Stop(_) => EventType::Stop, Event::Input { .. } => EventType::Input, Event::InputClosed { .. } => EventType::InputClosed, Event::Error(_) => EventType::Error, diff --git a/apis/python/node/Cargo.toml b/apis/python/node/Cargo.toml index 54ebff5c..f03a4036 100644 --- a/apis/python/node/Cargo.toml +++ b/apis/python/node/Cargo.toml @@ -21,10 +21,10 @@ dora-node-api = { workspace = true } dora-operator-api-python = { workspace = true } pyo3.workspace = true eyre = "0.6" -serde_yaml = "0.8.23" +serde_yaml = { workspace = true } flume = "0.10.14" dora-runtime = { workspace = true, features = ["tracing", "metrics", "python"] } -dora-daemon = { workspace = true } +dora-cli = { workspace = true } dora-download = { workspace = true } arrow = { workspace = true, features = ["pyarrow"] } pythonize = { workspace = true } @@ -33,6 +33,9 @@ dora-ros2-bridge-python = { workspace = true } # pyo3_special_method_derive = "0.4.2" tokio = { version = "1.24.2", features = ["rt"] } +[build-dependencies] +pyo3-build-config = "0.23" + [lib] name = "dora" crate-type = ["cdylib"] diff --git a/apis/python/node/build.rs b/apis/python/node/build.rs new file mode 100644 index 00000000..dace4a9b --- /dev/null +++ b/apis/python/node/build.rs @@ -0,0 +1,3 @@ +fn main() { + pyo3_build_config::add_extension_module_link_args(); +} diff --git a/apis/python/node/pyproject.toml b/apis/python/node/pyproject.toml index 33048a3f..8636df49 100644 --- a/apis/python/node/pyproject.toml +++ b/apis/python/node/pyproject.toml @@ -22,3 +22,11 @@ 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/apis/python/node/src/lib.rs b/apis/python/node/src/lib.rs index e2a249a9..18e70c3e 100644 --- a/apis/python/node/src/lib.rs +++ b/apis/python/node/src/lib.rs @@ -6,7 +6,6 @@ use std::sync::Arc; use std::time::Duration; use arrow::pyarrow::{FromPyArrow, ToPyArrow}; -use dora_daemon::Daemon; use dora_download::download_file; use dora_node_api::dora_core::config::NodeId; use dora_node_api::dora_core::descriptor::source_is_url; @@ -231,7 +230,7 @@ impl Node { /// :rtype: dict pub fn dataflow_descriptor(&mut self, py: Python) -> eyre::Result { Ok( - pythonize::pythonize(py, &self.node.get_mut().dataflow_descriptor()) + pythonize::pythonize(py, &self.node.get_mut().dataflow_descriptor()?) .map(|x| x.unbind())?, ) } @@ -382,19 +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<()> { - let dataflow_path = resolve_dataflow(dataflow_path).context("could not resolve dataflow")?; - let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .context("tokio runtime failed")?; - let result = rt.block_on(Daemon::run_dataflow(&dataflow_path, uv.unwrap_or_default()))?; - match result.is_ok() { - true => Ok(()), - false => Err(eyre::eyre!( - "Dataflow failed to run with error: {:?}", - result.node_results - )), - } + dora_cli::command::run(dataflow_path, uv.unwrap_or_default()) } #[pymodule] diff --git a/apis/python/operator/Cargo.toml b/apis/python/operator/Cargo.toml index a96c5987..a65a929d 100644 --- a/apis/python/operator/Cargo.toml +++ b/apis/python/operator/Cargo.toml @@ -14,7 +14,7 @@ repository.workspace = true dora-node-api = { workspace = true } pyo3 = { workspace = true, features = ["eyre", "abi3-py37"] } eyre = "0.6" -serde_yaml = "0.8.23" +serde_yaml = { workspace = true } flume = "0.10.14" arrow = { workspace = true, features = ["pyarrow"] } arrow-schema = { workspace = true } diff --git a/apis/python/operator/src/lib.rs b/apis/python/operator/src/lib.rs index cfc1e2bd..ea34b3b4 100644 --- a/apis/python/operator/src/lib.rs +++ b/apis/python/operator/src/lib.rs @@ -6,7 +6,7 @@ use std::{ use arrow::pyarrow::ToPyArrow; use dora_node_api::{ merged::{MergeExternalSend, MergedEvent}, - DoraNode, Event, EventStream, Metadata, MetadataParameters, Parameter, + DoraNode, Event, EventStream, Metadata, MetadataParameters, Parameter, StopCause, }; use eyre::{Context, Result}; use futures::{Stream, StreamExt}; @@ -146,7 +146,7 @@ impl PyEvent { fn ty(event: &Event) -> &str { match event { - Event::Stop => "STOP", + Event::Stop(_) => "STOP", Event::Input { .. } => "INPUT", Event::InputClosed { .. } => "INPUT_CLOSED", Event::Error(_) => "ERROR", @@ -158,6 +158,11 @@ impl PyEvent { match event { Event::Input { id, .. } => Some(id), Event::InputClosed { id } => Some(id), + Event::Stop(cause) => match cause { + StopCause::Manual => Some("MANUAL"), + StopCause::AllInputsClosed => Some("ALL_INPUTS_CLOSED"), + &_ => None, + }, _ => None, } } diff --git a/apis/rust/node/Cargo.toml b/apis/rust/node/Cargo.toml index a96256f0..d1485b4b 100644 --- a/apis/rust/node/Cargo.toml +++ b/apis/rust/node/Cargo.toml @@ -17,7 +17,7 @@ dora-core = { workspace = true } dora-message = { workspace = true } shared-memory-server = { workspace = true } eyre = "0.6.7" -serde_yaml = "0.8.23" +serde_yaml = { workspace = true } tracing = "0.1.33" flume = "0.10.14" bincode = "1.3.3" diff --git a/apis/rust/node/src/event_stream/event.rs b/apis/rust/node/src/event_stream/event.rs index b5d0e9b8..22997f4b 100644 --- a/apis/rust/node/src/event_stream/event.rs +++ b/apis/rust/node/src/event_stream/event.rs @@ -10,7 +10,7 @@ use shared_memory_extended::{Shmem, ShmemConf}; #[derive(Debug)] #[non_exhaustive] pub enum Event { - Stop, + Stop(StopCause), Reload { operator_id: Option, }, @@ -25,6 +25,13 @@ pub enum Event { Error(String), } +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum StopCause { + Manual, + AllInputsClosed, +} + pub enum RawData { Empty, Vec(AVec>), diff --git a/apis/rust/node/src/event_stream/mod.rs b/apis/rust/node/src/event_stream/mod.rs index 15c40e33..565f8713 100644 --- a/apis/rust/node/src/event_stream/mod.rs +++ b/apis/rust/node/src/event_stream/mod.rs @@ -11,7 +11,7 @@ use dora_message::{ node_to_daemon::{DaemonRequest, Timestamped}, DataflowId, }; -pub use event::{Event, MappedInputData, RawData}; +pub use event::{Event, MappedInputData, RawData, StopCause}; use futures::{ future::{select, Either}, Stream, StreamExt, @@ -199,7 +199,7 @@ impl EventStream { fn convert_event_item(item: EventItem) -> Event { match item { EventItem::NodeEvent { event, ack_channel } => match event { - NodeEvent::Stop => Event::Stop, + NodeEvent::Stop => Event::Stop(event::StopCause::Manual), NodeEvent::Reload { operator_id } => Event::Reload { operator_id }, NodeEvent::InputClosed { id } => Event::InputClosed { id }, NodeEvent::Input { id, metadata, data } => { @@ -234,13 +234,7 @@ impl EventStream { Err(err) => Event::Error(format!("{err:?}")), } } - NodeEvent::AllInputsClosed => { - let err = eyre!( - "received `AllInputsClosed` event, which should be handled by background task" - ); - tracing::error!("{err:?}"); - Event::Error(err.wrap_err("internal error").to_string()) - } + NodeEvent::AllInputsClosed => Event::Stop(event::StopCause::AllInputsClosed), }, EventItem::FatalError(err) => { diff --git a/apis/rust/node/src/event_stream/thread.rs b/apis/rust/node/src/event_stream/thread.rs index 5e982f74..a9dbba27 100644 --- a/apis/rust/node/src/event_stream/thread.rs +++ b/apis/rust/node/src/event_stream/thread.rs @@ -92,6 +92,7 @@ fn event_stream_loop( clock: Arc, ) { let mut tx = Some(tx); + let mut close_tx = false; let mut pending_drop_tokens: Vec<(DropToken, flume::Receiver<()>, Instant, u64)> = Vec::new(); let mut drop_tokens = Vec::new(); @@ -135,10 +136,8 @@ fn event_stream_loop( data: Some(data), .. } => data.drop_token(), NodeEvent::AllInputsClosed => { - // close the event stream - tx = None; - // skip this internal event - continue; + close_tx = true; + None } _ => None, }; @@ -166,6 +165,10 @@ fn event_stream_loop( } else { tracing::warn!("dropping event because event `tx` was already closed: `{inner:?}`"); } + + if close_tx { + tx = None; + }; } }; if let Err(err) = result { diff --git a/apis/rust/node/src/lib.rs b/apis/rust/node/src/lib.rs index 9836ab7b..e1b17b6f 100644 --- a/apis/rust/node/src/lib.rs +++ b/apis/rust/node/src/lib.rs @@ -20,7 +20,7 @@ pub use dora_message::{ metadata::{Metadata, MetadataParameters, Parameter}, DataflowId, }; -pub use event_stream::{merged, Event, EventStream, MappedInputData, RawData}; +pub use event_stream::{merged, Event, EventStream, MappedInputData, RawData, StopCause}; pub use flume::Receiver; pub use node::{arrow_utils, DataSample, DoraNode, ZERO_COPY_THRESHOLD}; 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 47890d46..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), @@ -60,7 +61,7 @@ pub struct DoraNode { drop_stream: DropStream, cache: VecDeque, - dataflow_descriptor: Descriptor, + dataflow_descriptor: serde_yaml::Result, warned_unknown_output: BTreeSet, _rt: TokioRuntime, } @@ -158,10 +159,9 @@ impl DoraNode { ), }; - let id = format!("{}/{}", dataflow_id, node_id); - #[cfg(feature = "metrics")] { + let id = format!("{}/{}", dataflow_id, node_id); let monitor_task = async move { if let Err(e) = run_metrics_monitor(id.clone()) .await @@ -200,7 +200,7 @@ impl DoraNode { sent_out_shared_memory: HashMap::new(), drop_stream, cache: VecDeque::new(), - dataflow_descriptor, + dataflow_descriptor: serde_yaml::from_value(dataflow_descriptor), warned_unknown_output: BTreeSet::new(), _rt: rt, }; @@ -449,8 +449,15 @@ impl DoraNode { /// Returns the full dataflow descriptor that this node is part of. /// /// This method returns the parsed dataflow YAML file. - pub fn dataflow_descriptor(&self) -> &Descriptor { - &self.dataflow_descriptor + pub fn dataflow_descriptor(&self) -> eyre::Result<&Descriptor> { + match &self.dataflow_descriptor { + Ok(d) => Ok(d), + Err(err) => eyre::bail!( + "failed to parse dataflow descriptor: {err}\n\n + This might be caused by mismatched version numbers of dora \ + daemon and the dora node API" + ), + } } } diff --git a/binaries/cli/Cargo.toml b/binaries/cli/Cargo.toml index 6fd7e1ce..7aa97db6 100644 --- a/binaries/cli/Cargo.toml +++ b/binaries/cli/Cargo.toml @@ -27,7 +27,7 @@ dora-node-api-c = { workspace = true } dora-operator-api-c = { workspace = true } dora-download = { workspace = true } serde = { version = "1.0.136", features = ["derive"] } -serde_yaml = "0.9.11" +serde_yaml = { workspace = true } webbrowser = "0.8.3" serde_json = "1.0.86" termcolor = "1.1.3" @@ -37,6 +37,7 @@ communication-layer-request-reply = { workspace = true } notify = "5.1.0" ctrlc = "3.2.5" tracing = "0.1.36" +tracing-log = "0.2.0" dora-tracing = { workspace = true, optional = true } bat = "0.24.0" dora-daemon = { workspace = true } @@ -50,7 +51,7 @@ tabwriter = "1.4.0" log = { version = "0.4.21", features = ["serde"] } colored = "2.1.0" env_logger = "0.11.3" -self_update = { version = "0.27.0", features = [ +self_update = { version = "0.42.0", features = [ "rustls", "archive-zip", "archive-tar", @@ -61,7 +62,11 @@ pyo3 = { workspace = true, features = [ "abi3", ], optional = true } self-replace = "1.5.0" +dunce = "1.0.5" +git2 = { workspace = true } +[build-dependencies] +pyo3-build-config = "0.23" [lib] name = "dora_cli" diff --git a/binaries/cli/build.rs b/binaries/cli/build.rs index 81caa36d..3672c16f 100644 --- a/binaries/cli/build.rs +++ b/binaries/cli/build.rs @@ -1,4 +1,5 @@ fn main() { + pyo3_build_config::add_extension_module_link_args(); println!( "cargo:rustc-env=TARGET={}", std::env::var("TARGET").unwrap() diff --git a/binaries/cli/pyproject.toml b/binaries/cli/pyproject.toml index 1ef4af39..c2d52457 100644 --- a/binaries/cli/pyproject.toml +++ b/binaries/cli/pyproject.toml @@ -15,6 +15,14 @@ features = ["python", "pyo3/extension-module"] [tool.ruff.lint] extend-select = [ - "D", # pydocstyle - "UP" + "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/src/build.rs b/binaries/cli/src/build.rs deleted file mode 100644 index 3e8ffb6d..00000000 --- a/binaries/cli/src/build.rs +++ /dev/null @@ -1,116 +0,0 @@ -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}; - -use crate::resolve_dataflow; - -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..1fd1ed91 --- /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:?}"), + } +} 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..7f6c2557 --- /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(), + }) + } +} diff --git a/binaries/cli/src/command/build/mod.rs b/binaries/cli/src/command/build/mod.rs new file mode 100644 index 00000000..e0046666 --- /dev/null +++ b/binaries/cli/src/command/build/mod.rs @@ -0,0 +1,168 @@ +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; + +use crate::{connect_to_coordinator, resolve_dataflow, session::DataflowSession}; + +use distributed::{build_distributed_dataflow, wait_until_dataflow_built}; +use local::build_dataflow_locally; + +mod distributed; +mod git; +mod 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 = super::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() +} diff --git a/binaries/cli/src/check.rs b/binaries/cli/src/command/check.rs similarity index 100% rename from binaries/cli/src/check.rs rename to binaries/cli/src/command/check.rs diff --git a/binaries/cli/src/logs.rs b/binaries/cli/src/command/logs.rs similarity index 100% rename from binaries/cli/src/logs.rs rename to binaries/cli/src/command/logs.rs diff --git a/binaries/cli/src/command/mod.rs b/binaries/cli/src/command/mod.rs new file mode 100644 index 00000000..1ef93c08 --- /dev/null +++ b/binaries/cli/src/command/mod.rs @@ -0,0 +1,60 @@ +pub use build::build; +pub use logs::logs; +pub use run::run; +pub use start::start; + +use std::path::{Path, PathBuf}; + +use communication_layer_request_reply::TcpRequestReplyConnection; +use dora_core::descriptor::Descriptor; +use dora_message::{cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply}; +use eyre::{bail, Context, ContextCompat}; + +mod build; +pub mod check; +mod logs; +mod run; +mod start; +pub mod up; + +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 + }, + ) +} + +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")?; + + let result: ControlRequestReply = + serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; + match result { + ControlRequestReply::CliAndDefaultDaemonIps { + default_daemon, + cli, + } => Ok(default_daemon.is_some() && default_daemon == cli), + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected start dataflow reply: {other:?}"), + } +} diff --git a/binaries/cli/src/command/run.rs b/binaries/cli/src/command/run.rs new file mode 100644 index 00000000..7b1adba7 --- /dev/null +++ b/binaries/cli/src/command/run.rs @@ -0,0 +1,34 @@ +use dora_daemon::{flume, Daemon, LogDestination}; +use eyre::Context; +use tokio::runtime::Builder; + +use crate::{ + handle_dataflow_result, output::print_log_message, resolve_dataflow, session::DataflowSession, +}; + +pub fn run(dataflow: String, uv: bool) -> Result<(), eyre::Error> { + 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 (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) +} diff --git a/binaries/cli/src/attach.rs b/binaries/cli/src/command/start/attach.rs similarity index 82% rename from binaries/cli/src/attach.rs rename to binaries/cli/src/command/start/attach.rs index 39c3a056..a32994d0 100644 --- a/binaries/cli/src/attach.rs +++ b/binaries/cli/src/command/start/attach.rs @@ -1,4 +1,3 @@ -use colored::Colorize; use communication_layer_request_reply::{TcpConnection, TcpRequestReplyConnection}; use dora_core::descriptor::{resolve_path, CoreNodeKind, Descriptor, DescriptorExt}; use dora_message::cli_to_coordinator::ControlRequest; @@ -16,6 +15,7 @@ use tracing::{error, info}; use uuid::Uuid; use crate::handle_dataflow_result; +use crate::output::print_log_message; pub fn attach_dataflow( dataflow: Descriptor, @@ -33,6 +33,8 @@ pub fn attach_dataflow( let nodes = dataflow.resolve_aliases_and_set_defaults()?; + let print_daemon_name = nodes.values().any(|n| n.deploy.is_some()); + let working_dir = dataflow_path .canonicalize() .context("failed to canonicalize dataflow path")? @@ -155,39 +157,7 @@ pub fn attach_dataflow( }, Ok(AttachEvent::Control(control_request)) => control_request, Ok(AttachEvent::Log(Ok(log_message))) => { - let LogMessage { - dataflow_id, - node_id, - daemon_id, - level, - target, - module_path: _, - file: _, - line: _, - message, - } = log_message; - let level = match level { - log::Level::Error => "ERROR".red(), - log::Level::Warn => "WARN ".yellow(), - log::Level::Info => "INFO ".green(), - other => format!("{other:5}").normal(), - }; - let dataflow = format!(" dataflow `{dataflow_id}`").cyan(); - let daemon = match daemon_id { - Some(id) => format!(" on daemon `{id}`"), - None => " on default daemon".to_string(), - } - .bright_black(); - let node = match node_id { - Some(node_id) => format!(" {node_id}").bold(), - None => "".normal(), - }; - let target = match target { - Some(target) => format!(" {target}").dimmed(), - None => "".normal(), - }; - - println!("{level}{dataflow}{daemon}{node}{target}: {message}"); + print_log_message(log_message, false, print_daemon_name); continue; } Ok(AttachEvent::Log(Err(err))) => { @@ -202,7 +172,7 @@ pub fn attach_dataflow( let result: ControlRequestReply = serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?; match result { - ControlRequestReply::DataflowStarted { uuid: _ } => (), + ControlRequestReply::DataflowSpawned { uuid: _ } => (), ControlRequestReply::DataflowStopped { uuid, result } => { info!("dataflow {uuid} stopped"); break handle_dataflow_result(result, Some(uuid)); diff --git a/binaries/cli/src/command/start/mod.rs b/binaries/cli/src/command/start/mod.rs new file mode 100644 index 00000000..72464f86 --- /dev/null +++ b/binaries/cli/src/command/start/mod.rs @@ -0,0 +1,170 @@ +use communication_layer_request_reply::{TcpConnection, TcpRequestReplyConnection}; +use dora_core::descriptor::{Descriptor, DescriptorExt}; +use dora_message::{ + cli_to_coordinator::ControlRequest, common::LogMessage, coordinator_to_cli::ControlRequestReply, +}; +use eyre::{bail, Context}; +use std::{ + net::{SocketAddr, TcpStream}, + path::PathBuf, +}; +use uuid::Uuid; + +use crate::{ + connect_to_coordinator, output::print_log_message, resolve_dataflow, session::DataflowSession, +}; +use attach::attach_dataflow; + +mod attach; + +pub fn start( + dataflow: String, + name: Option, + coordinator_socket: SocketAddr, + attach: bool, + detach: bool, + hot_reload: bool, + uv: bool, +) -> eyre::Result<()> { + let (dataflow, dataflow_descriptor, mut session, dataflow_id) = + start_dataflow(dataflow, name, coordinator_socket, 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 { + let log_level = env_logger::Builder::new() + .filter_level(log::LevelFilter::Info) + .parse_default_env() + .build() + .filter(); + + attach_dataflow( + dataflow_descriptor, + dataflow, + dataflow_id, + &mut *session, + hot_reload, + coordinator_socket, + log_level, + ) + } else { + let print_daemon_name = dataflow_descriptor.nodes.iter().any(|n| n.deploy.is_some()); + // wait until dataflow is started + wait_until_dataflow_started( + dataflow_id, + &mut session, + coordinator_socket, + log::LevelFilter::Info, + print_daemon_name, + ) + } +} + +fn start_dataflow( + dataflow: String, + name: Option, + coordinator_socket: SocketAddr, + uv: bool, +) -> Result<(PathBuf, Descriptor, Box, Uuid), eyre::Error> { + 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 dataflow_session = + DataflowSession::read_session(&dataflow).context("failed to read DataflowSession")?; + + let mut session = connect_to_coordinator(coordinator_socket) + .wrap_err("failed to connect to dora coordinator")?; + + let local_working_dir = + super::local_working_dir(&dataflow, &dataflow_descriptor, &mut *session)?; + + let dataflow_id = { + let dataflow = dataflow_descriptor.clone(); + let session: &mut TcpRequestReplyConnection = &mut *session; + let reply_raw = session + .request( + &serde_json::to_vec(&ControlRequest::Start { + build_id: dataflow_session.build_id, + session_id: dataflow_session.session_id, + 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::DataflowStartTriggered { uuid } => { + eprintln!("dataflow start triggered: {uuid}"); + uuid + } + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected start dataflow reply: {other:?}"), + } + }; + Ok((dataflow, dataflow_descriptor, session, dataflow_id)) +} + +fn wait_until_dataflow_started( + dataflow_id: Uuid, + session: &mut Box, + coordinator_addr: SocketAddr, + log_level: log::LevelFilter, + print_daemon_id: bool, +) -> eyre::Result<()> { + // subscribe to log messages + let mut log_session = TcpConnection { + stream: TcpStream::connect(coordinator_addr) + .wrap_err("failed to connect to dora coordinator")?, + }; + log_session + .send( + &serde_json::to_vec(&ControlRequest::LogSubscribe { + dataflow_id, + level: log_level, + }) + .wrap_err("failed to serialize message")?, + ) + .wrap_err("failed to send 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, print_daemon_id); + } + Err(err) => { + tracing::warn!("failed to parse log message: {err:?}") + } + } + } + }); + + let reply_raw = session + .request(&serde_json::to_vec(&ControlRequest::WaitForSpawn { dataflow_id }).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::DataflowSpawned { uuid } => { + eprintln!("dataflow started: {uuid}"); + } + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected start dataflow reply: {other:?}"), + } + Ok(()) +} diff --git a/binaries/cli/src/up.rs b/binaries/cli/src/command/up.rs similarity index 98% rename from binaries/cli/src/up.rs rename to binaries/cli/src/command/up.rs index 16f1a4c1..03eead4a 100644 --- a/binaries/cli/src/up.rs +++ b/binaries/cli/src/command/up.rs @@ -1,4 +1,4 @@ -use crate::{check::daemon_running, connect_to_coordinator, LOCALHOST}; +use crate::{command::check::daemon_running, 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}; diff --git a/binaries/cli/src/lib.rs b/binaries/cli/src/lib.rs index cfe9e41b..9c3cfd41 100644 --- a/binaries/cli/src/lib.rs +++ b/binaries/cli/src/lib.rs @@ -1,4 +1,3 @@ -use attach::attach_dataflow; use colored::Colorize; use communication_layer_request_reply::{RequestReplyLayer, TcpLayer, TcpRequestReplyConnection}; use dora_coordinator::Event; @@ -9,7 +8,7 @@ use dora_core::{ DORA_DAEMON_LOCAL_LISTEN_PORT_DEFAULT, }, }; -use dora_daemon::Daemon; +use dora_daemon::{Daemon, LogDestination}; use dora_download::download_file; use dora_message::{ cli_to_coordinator::ControlRequest, @@ -31,14 +30,12 @@ use tokio::runtime::Builder; use tracing::level_filters::LevelFilter; use uuid::Uuid; -mod attach; -mod build; -mod check; +pub mod command; mod formatting; mod graph; -mod logs; +pub mod output; +pub mod session; 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)); @@ -82,9 +79,18 @@ enum Command { /// 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, }, /// Generate a new project or node. Choose the language between Rust, Python, C or C++. New { @@ -292,14 +298,16 @@ enum Lang { } pub fn lib_main(args: Args) { - if let Err(err) = run(args) { + if let Err(err) = run_cli(args) { eprintln!("\n\n{}", "[ERROR]".bold().red()); eprintln!("{err:?}"); std::process::exit(1); } } -fn run(args: Args) -> eyre::Result<()> { +fn run_cli(args: Args) -> eyre::Result<()> { + tracing_log::LogTracer::init()?; + #[cfg(feature = "tracing")] match &args.command { Command::Daemon { @@ -334,7 +342,7 @@ fn run(args: Args) -> eyre::Result<()> { .build() .wrap_err("failed to set up tracing subscriber")?; } - Command::Run { .. } => { + Command::Run { .. } | Command::Build { .. } => { let log_level = std::env::var("RUST_LOG").ok().unwrap_or("info".to_string()); TracingBuilder::new("run") .with_stdout(log_level) @@ -349,12 +357,6 @@ fn run(args: Args) -> eyre::Result<()> { } }; - let log_level = env_logger::Builder::new() - .filter_level(log::LevelFilter::Info) - .parse_default_env() - .build() - .filter(); - match args.command { Command::Check { dataflow, @@ -369,9 +371,9 @@ fn run(args: Args) -> eyre::Result<()> { .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())? + command::check::check_environment((coordinator_addr, coordinator_port).into())? } - None => check::check_environment((coordinator_addr, coordinator_port).into())?, + None => command::check::check_environment((coordinator_addr, coordinator_port).into())?, }, Command::Graph { dataflow, @@ -380,24 +382,20 @@ fn run(args: Args) -> eyre::Result<()> { } => { graph::create(dataflow, mermaid, open)?; } - Command::Build { dataflow, uv } => { - build::build(dataflow, uv)?; - } + Command::Build { + dataflow, + coordinator_addr, + coordinator_port, + uv, + local, + } => command::build(dataflow, coordinator_addr, coordinator_port, uv, local)?, 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::Run { dataflow, uv } => command::run(dataflow, uv)?, Command::Up { config } => { - up::up(config.as_deref())?; + command::up::up(config.as_deref())?; } Command::Logs { dataflow, @@ -412,15 +410,16 @@ fn run(args: Args) -> eyre::Result<()> { 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)? + command::logs(&mut *session, uuid, name, node)? } else { - let active = list.get_active(); + let active: Vec = + 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::logs(&mut *session, Some(uuid.uuid), None, node)? } } Command::Start { @@ -433,48 +432,16 @@ fn run(args: Args) -> eyre::Result<()> { 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(), + command::start( + dataflow, name, - working_dir, - &mut *session, + coordinator_socket, + attach, + detach, + hot_reload, 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, @@ -504,7 +471,7 @@ fn run(args: Args) -> eyre::Result<()> { config, coordinator_addr, coordinator_port, - } => up::destroy( + } => command::up::destroy( config.as_deref(), (coordinator_addr, coordinator_port).into(), )?, @@ -554,8 +521,13 @@ fn run(args: Args) -> eyre::Result<()> { coordinator_addr ); } + let dataflow_session = + DataflowSession::read_session(&dataflow_path).context("failed to read DataflowSession")?; - let result = Daemon::run_dataflow(&dataflow_path, false).await?; + let result = Daemon::run_dataflow(&dataflow_path, + dataflow_session.build_id, dataflow_session.local_build, dataflow_session.session_id, false, + LogDestination::Tracing, + ).await?; handle_dataflow_result(result, None) } None => { @@ -682,37 +654,6 @@ fn run(args: Args) -> eyre::Result<()> { 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, @@ -863,6 +804,8 @@ use pyo3::{ wrap_pyfunction, Bound, PyResult, Python, }; +use crate::session::DataflowSession; + #[cfg(feature = "python")] #[pyfunction] fn py_main(_py: Python) -> PyResult<()> { diff --git a/binaries/cli/src/output.rs b/binaries/cli/src/output.rs new file mode 100644 index 00000000..ff5ba755 --- /dev/null +++ b/binaries/cli/src/output.rs @@ -0,0 +1,62 @@ +use colored::Colorize; +use dora_core::build::LogLevelOrStdout; +use dora_message::common::LogMessage; + +pub fn print_log_message( + log_message: LogMessage, + print_dataflow_id: bool, + print_daemon_name: bool, +) { + let LogMessage { + build_id: _, + dataflow_id, + node_id, + daemon_id, + level, + target, + module_path: _, + file: _, + line: _, + message, + } = log_message; + let level = match level { + 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".bright_blue().italic().dimmed(), + }; + + let dataflow = match dataflow_id { + Some(dataflow_id) if print_dataflow_id => format!("dataflow `{dataflow_id}` ").cyan(), + _ => String::new().cyan(), + }; + let daemon = match daemon_id { + Some(id) if print_daemon_name => match id.machine_id() { + Some(machine_id) => format!("on daemon `{machine_id}`"), + None => "on default daemon ".to_string(), + }, + None if print_daemon_name => "on default daemon".to_string(), + _ => String::new(), + } + .bright_black(); + let colon = ":".bright_black().bold(); + let node = match node_id { + Some(node_id) => { + let node_id = node_id.to_string().dimmed().bold(); + let padding = if daemon.is_empty() { "" } else { " " }; + format!("{node_id}{padding}{daemon}{colon} ") + } + None if daemon.is_empty() => "".into(), + None => format!("{daemon}{colon} "), + }; + let target = match target { + Some(target) => format!("{target} ").dimmed(), + None => "".normal(), + }; + + println!("{node}{level} {target}{dataflow} {message}"); +} diff --git a/binaries/cli/src/session.rs b/binaries/cli/src/session.rs new file mode 100644 index 00000000..9a8ac5b8 --- /dev/null +++ b/binaries/cli/src/session.rs @@ -0,0 +1,98 @@ +use std::{ + collections::BTreeMap, + path::{Path, PathBuf}, +}; + +use dora_core::build::BuildInfo; +use dora_message::{common::GitSource, id::NodeId, BuildId, SessionId}; +use eyre::{Context, ContextCompat}; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct DataflowSession { + pub build_id: Option, + pub session_id: SessionId, + pub git_sources: BTreeMap, + pub local_build: Option, +} + +impl Default for DataflowSession { + fn default() -> Self { + Self { + build_id: None, + session_id: SessionId::generate(), + git_sources: Default::default(), + local_build: Default::default(), + } + } +} + +impl DataflowSession { + pub fn read_session(dataflow_path: &Path) -> eyre::Result { + let session_file = session_file_path(dataflow_path)?; + if session_file.exists() { + if let Ok(parsed) = deserialize(&session_file) { + return Ok(parsed); + } else { + tracing::warn!("failed to read dataflow session file, regenerating (you might need to run `dora build` again)"); + } + } + + let default_session = DataflowSession::default(); + default_session.write_out_for_dataflow(dataflow_path)?; + Ok(default_session) + } + + pub fn write_out_for_dataflow(&self, dataflow_path: &Path) -> eyre::Result<()> { + let session_file = session_file_path(dataflow_path)?; + let filename = session_file + .file_name() + .context("session file has no file name")? + .to_str() + .context("session file name is no utf8")?; + if let Some(parent) = session_file.parent() { + std::fs::create_dir_all(parent).context("failed to create out dir")?; + } + std::fs::write(&session_file, self.serialize()?) + .context("failed to write dataflow session file")?; + let gitignore = session_file.with_file_name(".gitignore"); + if gitignore.exists() { + let existing = + std::fs::read_to_string(&gitignore).context("failed to read gitignore")?; + if !existing + .lines() + .any(|l| l.split_once('/') == Some(("", filename))) + { + let new = existing + &format!("\n/{filename}\n"); + std::fs::write(gitignore, new).context("failed to update gitignore")?; + } + } else { + std::fs::write(gitignore, format!("/{filename}\n")) + .context("failed to write gitignore")?; + } + Ok(()) + } + + fn serialize(&self) -> eyre::Result { + serde_yaml::to_string(&self).context("failed to serialize dataflow session file") + } +} + +fn deserialize(session_file: &Path) -> eyre::Result { + std::fs::read_to_string(session_file) + .context("failed to read DataflowSession file") + .and_then(|s| { + serde_yaml::from_str(&s).context("failed to deserialize DataflowSession file") + }) +} + +fn session_file_path(dataflow_path: &Path) -> eyre::Result { + let file_stem = dataflow_path + .file_stem() + .wrap_err("dataflow path has no file stem")? + .to_str() + .wrap_err("dataflow file stem is not valid utf-8")?; + let session_file = dataflow_path + .with_file_name("out") + .join(format!("{file_stem}.dora-session.yaml")); + Ok(session_file) +} diff --git a/binaries/cli/src/template/c/cmake-template.txt b/binaries/cli/src/template/c/cmake-template.txt index 32cb561f..eafe50da 100644 --- a/binaries/cli/src/template/c/cmake-template.txt +++ b/binaries/cli/src/template/c/cmake-template.txt @@ -64,16 +64,16 @@ link_directories(${dora_link_dirs}) add_executable(talker_1 talker_1/node.c) add_dependencies(talker_1 Dora_c) target_include_directories(talker_1 PRIVATE ${dora_c_include_dir}) -target_link_libraries(talker_1 dora_node_api_c m) +target_link_libraries(talker_1 dora_node_api_c m z) add_executable(talker_2 talker_2/node.c) add_dependencies(talker_2 Dora_c) target_include_directories(talker_2 PRIVATE ${dora_c_include_dir}) -target_link_libraries(talker_2 dora_node_api_c m) +target_link_libraries(talker_2 dora_node_api_c m z) add_executable(listener_1 listener_1/node.c) add_dependencies(listener_1 Dora_c) target_include_directories(listener_1 PRIVATE ${dora_c_include_dir}) -target_link_libraries(listener_1 dora_node_api_c m) +target_link_libraries(listener_1 dora_node_api_c m z) -install(TARGETS listener_1 talker_1 talker_2 DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin) \ No newline at end of file +install(TARGETS listener_1 talker_1 talker_2 DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin) diff --git a/binaries/cli/src/template/cxx/cmake-template.txt b/binaries/cli/src/template/cxx/cmake-template.txt index bd3fe492..7f7ce865 100644 --- a/binaries/cli/src/template/cxx/cmake-template.txt +++ b/binaries/cli/src/template/cxx/cmake-template.txt @@ -70,16 +70,16 @@ link_directories(${dora_link_dirs}) add_executable(talker_1 talker_1/node.cc ${node_bridge}) add_dependencies(talker_1 Dora_cxx) target_include_directories(talker_1 PRIVATE ${dora_cxx_include_dir}) -target_link_libraries(talker_1 dora_node_api_cxx) +target_link_libraries(talker_1 dora_node_api_cxx z) add_executable(talker_2 talker_2/node.cc ${node_bridge}) add_dependencies(talker_2 Dora_cxx) target_include_directories(talker_2 PRIVATE ${dora_cxx_include_dir}) -target_link_libraries(talker_2 dora_node_api_cxx) +target_link_libraries(talker_2 dora_node_api_cxx z) add_executable(listener_1 listener_1/node.cc ${node_bridge}) add_dependencies(listener_1 Dora_cxx) target_include_directories(listener_1 PRIVATE ${dora_cxx_include_dir}) -target_link_libraries(listener_1 dora_node_api_cxx) +target_link_libraries(listener_1 dora_node_api_cxx z) -install(TARGETS listener_1 talker_1 talker_2 DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin) \ No newline at end of file +install(TARGETS listener_1 talker_1 talker_2 DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin) 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" diff --git a/binaries/coordinator/src/control.rs b/binaries/coordinator/src/control.rs index f7cb23c1..446233a8 100644 --- a/binaries/coordinator/src/control.rs +++ b/binaries/coordinator/src/control.rs @@ -2,7 +2,9 @@ use crate::{ tcp_utils::{tcp_receive, tcp_send}, Event, }; -use dora_message::{cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply}; +use dora_message::{ + cli_to_coordinator::ControlRequest, coordinator_to_cli::ControlRequestReply, BuildId, +}; use eyre::{eyre, Context}; use futures::{ future::{self, Either}, @@ -79,6 +81,7 @@ async fn handle_requests( tx: mpsc::Sender, _finish_tx: mpsc::Sender<()>, ) { + let peer_addr = connection.peer_addr().ok(); loop { let next_request = tcp_receive(&mut connection).map(Either::Left); let coordinator_stopped = tx.closed().map(Either::Right); @@ -114,11 +117,29 @@ async fn handle_requests( break; } - let result = match request { + if let Ok(ControlRequest::BuildLogSubscribe { build_id, level }) = request { + let _ = tx + .send(ControlEvent::BuildLogSubscribe { + build_id, + level, + connection, + }) + .await; + break; + } + + let mut result = match request { Ok(request) => handle_request(request, &tx).await, Err(err) => Err(err), }; + if let Ok(ControlRequestReply::CliAndDefaultDaemonIps { cli, .. }) = &mut result { + if cli.is_none() { + // fill cli IP address in reply + *cli = peer_addr.map(|s| s.ip()); + } + } + let reply = result.unwrap_or_else(|err| ControlRequestReply::Error(format!("{err:?}"))); let serialized: Vec = match serde_json::to_vec(&reply).wrap_err("failed to serialize ControlRequestReply") { @@ -155,7 +176,7 @@ async fn handle_request( ) -> eyre::Result { let (reply_tx, reply_rx) = oneshot::channel(); let event = ControlEvent::IncomingRequest { - request, + request: request.clone(), reply_sender: reply_tx, }; @@ -165,7 +186,7 @@ async fn handle_request( reply_rx .await - .unwrap_or(Ok(ControlRequestReply::CoordinatorStopped)) + .wrap_err_with(|| format!("no coordinator reply to {request:?}"))? } #[derive(Debug)] @@ -179,6 +200,11 @@ pub enum ControlEvent { level: log::LevelFilter, connection: TcpStream, }, + BuildLogSubscribe { + build_id: BuildId, + level: log::LevelFilter, + connection: TcpStream, + }, Error(eyre::Report), } diff --git a/binaries/coordinator/src/lib.rs b/binaries/coordinator/src/lib.rs index e002f859..e3a8d767 100644 --- a/binaries/coordinator/src/lib.rs +++ b/binaries/coordinator/src/lib.rs @@ -5,22 +5,27 @@ use crate::{ pub use control::ControlEvent; use dora_core::{ config::{NodeId, OperatorId}, + descriptor::DescriptorExt, uhlc::{self, HLC}, }; use dora_message::{ cli_to_coordinator::ControlRequest, - common::DaemonId, + common::{DaemonId, GitSource}, coordinator_to_cli::{ ControlRequestReply, DataflowIdAndName, DataflowList, DataflowListEntry, DataflowResult, DataflowStatus, LogLevel, LogMessage, }, - coordinator_to_daemon::{DaemonCoordinatorEvent, RegisterResult, Timestamped}, + coordinator_to_daemon::{ + BuildDataflowNodes, DaemonCoordinatorEvent, RegisterResult, Timestamped, + }, daemon_to_coordinator::{DaemonCoordinatorReply, DataflowDaemonResult}, descriptor::{Descriptor, ResolvedNode}, + BuildId, DataflowId, SessionId, }; use eyre::{bail, eyre, ContextCompat, Result, WrapErr}; use futures::{future::join_all, stream::FuturesUnordered, Future, Stream, StreamExt}; use futures_concurrency::stream::Merge; +use itertools::Itertools; use log_subscriber::LogSubscriber; use run::SpawnedDataflow; use std::{ @@ -30,7 +35,11 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use tokio::{net::TcpStream, sync::mpsc, task::JoinHandle}; +use tokio::{ + net::TcpStream, + sync::{mpsc, oneshot}, + task::JoinHandle, +}; use tokio_stream::wrappers::{ReceiverStream, TcpListenerStream}; use uuid::Uuid; @@ -135,6 +144,10 @@ impl DaemonConnections { } } + fn get(&self, id: &DaemonId) -> Option<&DaemonConnection> { + self.daemons.get(id) + } + fn get_mut(&mut self, id: &DaemonId) -> Option<&mut DaemonConnection> { self.daemons.get_mut(id) } @@ -157,10 +170,6 @@ impl DaemonConnections { self.daemons.keys() } - fn iter(&self) -> impl Iterator { - self.daemons.iter() - } - fn iter_mut(&mut self) -> impl Iterator { self.daemons.iter_mut() } @@ -194,13 +203,20 @@ async fn start_inner( let mut events = (abortable_events, daemon_events).merge(); - let mut running_dataflows: HashMap = HashMap::new(); - let mut dataflow_results: HashMap> = + let mut running_builds: HashMap = HashMap::new(); + let mut finished_builds: HashMap = HashMap::new(); + + let mut running_dataflows: HashMap = HashMap::new(); + let mut dataflow_results: HashMap> = HashMap::new(); - let mut archived_dataflows: HashMap = HashMap::new(); + let mut archived_dataflows: HashMap = HashMap::new(); let mut daemon_connections = DaemonConnections::default(); while let Some(event) = events.next().await { + // used below for measuring the event handling duration + let start = Instant::now(); + let event_kind = event.kind(); + if event.log() { tracing::trace!("Handling event {event:?}"); } @@ -347,12 +363,13 @@ async fn start_inner( let mut finished_dataflow = entry.remove(); let dataflow_id = finished_dataflow.uuid; send_log_message( - &mut finished_dataflow, + &mut finished_dataflow.log_subscribers, &LogMessage { - dataflow_id, + build_id: None, + dataflow_id: Some(dataflow_id), node_id: None, daemon_id: None, - level: LogLevel::Info, + level: LogLevel::Info.into(), target: Some("coordinator".into()), module_path: None, file: None, @@ -371,9 +388,15 @@ async fn start_inner( DataflowResult::ok_empty(uuid, clock.new_timestamp()) }), }; - for sender in finished_dataflow.reply_senders { + for sender in finished_dataflow.stop_reply_senders { let _ = sender.send(Ok(reply.clone())); } + if !matches!( + finished_dataflow.spawn_result, + CachedResult::Cached { .. } + ) { + log::error!("pending spawn result on dataflow finish"); + } } } std::collections::hash_map::Entry::Vacant(_) => { @@ -389,7 +412,54 @@ async fn start_inner( reply_sender, } => { match request { + ControlRequest::Build { + session_id, + dataflow, + git_sources, + prev_git_sources, + local_working_dir, + uv, + } => { + // assign a random build id + let build_id = BuildId::generate(); + + let result = build_dataflow( + build_id, + session_id, + dataflow, + git_sources, + prev_git_sources, + local_working_dir, + &clock, + uv, + &mut daemon_connections, + ) + .await; + match result { + Ok(build) => { + running_builds.insert(build_id, build); + let _ = reply_sender.send(Ok( + ControlRequestReply::DataflowBuildTriggered { build_id }, + )); + } + Err(err) => { + let _ = reply_sender.send(Err(err)); + } + } + } + ControlRequest::WaitForBuild { build_id } => { + if let Some(build) = running_builds.get_mut(&build_id) { + build.build_result.register(reply_sender); + } else if let Some(result) = finished_builds.get_mut(&build_id) { + result.register(reply_sender); + } else { + let _ = + reply_sender.send(Err(eyre!("unknown build id {build_id}"))); + } + } ControlRequest::Start { + build_id, + session_id, dataflow, name, local_working_dir, @@ -408,6 +478,8 @@ async fn start_inner( } } let dataflow = start_dataflow( + build_id, + session_id, dataflow, local_working_dir, name, @@ -418,16 +490,30 @@ async fn start_inner( .await?; Ok(dataflow) }; - let reply = inner.await.map(|dataflow| { - let uuid = dataflow.uuid; - running_dataflows.insert(uuid, dataflow); - ControlRequestReply::DataflowStarted { uuid } - }); - let _ = reply_sender.send(reply); + match inner.await { + Ok(dataflow) => { + let uuid = dataflow.uuid; + running_dataflows.insert(uuid, dataflow); + let _ = reply_sender.send(Ok( + ControlRequestReply::DataflowStartTriggered { uuid }, + )); + } + Err(err) => { + let _ = reply_sender.send(Err(err)); + } + } + } + ControlRequest::WaitForSpawn { dataflow_id } => { + if let Some(dataflow) = running_dataflows.get_mut(&dataflow_id) { + dataflow.spawn_result.register(reply_sender); + } else { + let _ = + reply_sender.send(Err(eyre!("unknown dataflow {dataflow_id}"))); + } } ControlRequest::Check { dataflow_uuid } => { let status = match &running_dataflows.get(&dataflow_uuid) { - Some(_) => ControlRequestReply::DataflowStarted { + Some(_) => ControlRequestReply::DataflowSpawned { uuid: dataflow_uuid, }, None => ControlRequestReply::DataflowStopped { @@ -495,7 +581,7 @@ async fn start_inner( match dataflow { Ok(dataflow) => { - dataflow.reply_senders.push(reply_sender); + dataflow.stop_reply_senders.push(reply_sender); } Err(err) => { let _ = reply_sender.send(Err(err)); @@ -528,7 +614,7 @@ async fn start_inner( match dataflow { Ok(dataflow) => { - dataflow.reply_senders.push(reply_sender); + dataflow.stop_reply_senders.push(reply_sender); } Err(err) => { let _ = reply_sender.send(Err(err)); @@ -626,6 +712,27 @@ async fn start_inner( "LogSubscribe request should be handled separately" ))); } + ControlRequest::BuildLogSubscribe { .. } => { + let _ = reply_sender.send(Err(eyre::eyre!( + "BuildLogSubscribe request should be handled separately" + ))); + } + ControlRequest::CliAndDefaultDaemonOnSameMachine => { + let mut default_daemon_ip = None; + if let Some(default_id) = daemon_connections.unnamed().next() { + if let Some(connection) = daemon_connections.get(default_id) { + if let Ok(addr) = connection.stream.peer_addr() { + default_daemon_ip = Some(addr.ip()); + } + } + } + let _ = reply_sender.send(Ok( + ControlRequestReply::CliAndDefaultDaemonIps { + default_daemon: default_daemon_ip, + cli: None, // filled later + }, + )); + } } } ControlEvent::Error(err) => tracing::error!("{err:?}"), @@ -640,6 +747,17 @@ async fn start_inner( .push(LogSubscriber::new(level, connection)); } } + ControlEvent::BuildLogSubscribe { + build_id, + level, + connection, + } => { + if let Some(build) = running_builds.get_mut(&build_id) { + build + .log_subscribers + .push(LogSubscriber::new(level, connection)); + } + } }, Event::DaemonHeartbeatInterval => { let mut disconnected = BTreeSet::new(); @@ -695,14 +813,89 @@ async fn start_inner( } } Event::Log(message) => { - if let Some(dataflow) = running_dataflows.get_mut(&message.dataflow_id) { - send_log_message(dataflow, &message).await; + if let Some(dataflow_id) = &message.dataflow_id { + if let Some(dataflow) = running_dataflows.get_mut(dataflow_id) { + send_log_message(&mut dataflow.log_subscribers, &message).await; + } + } + if let Some(build_id) = message.build_id { + if let Some(build) = running_builds.get_mut(&build_id) { + send_log_message(&mut build.log_subscribers, &message).await; + } } } Event::DaemonExit { daemon_id } => { tracing::info!("Daemon `{daemon_id}` exited"); daemon_connections.remove(&daemon_id); } + Event::DataflowBuildResult { + build_id, + daemon_id, + result, + } => match running_builds.get_mut(&build_id) { + Some(build) => { + build.pending_build_results.remove(&daemon_id); + match result { + Ok(()) => {} + Err(err) => { + build.errors.push(format!("{err:?}")); + } + }; + if build.pending_build_results.is_empty() { + tracing::info!("dataflow build finished: `{build_id}`"); + let mut build = running_builds.remove(&build_id).unwrap(); + let result = if build.errors.is_empty() { + Ok(()) + } else { + Err(format!("build failed: {}", build.errors.join("\n\n"))) + }; + + build.build_result.set_result(Ok( + ControlRequestReply::DataflowBuildFinished { build_id, result }, + )); + + finished_builds.insert(build_id, build.build_result); + } + } + None => { + tracing::warn!("received DataflowSpawnResult, but no matching dataflow in `running_dataflows` map"); + } + }, + Event::DataflowSpawnResult { + dataflow_id, + daemon_id, + result, + } => match running_dataflows.get_mut(&dataflow_id) { + Some(dataflow) => { + dataflow.pending_spawn_results.remove(&daemon_id); + match result { + Ok(()) => { + if dataflow.pending_spawn_results.is_empty() { + tracing::info!("successfully spawned dataflow `{dataflow_id}`",); + dataflow.spawn_result.set_result(Ok( + ControlRequestReply::DataflowSpawned { uuid: dataflow_id }, + )); + } + } + Err(err) => { + tracing::warn!("error while spawning dataflow `{dataflow_id}`"); + dataflow.spawn_result.set_result(Err(err)); + } + }; + } + None => { + tracing::warn!("received DataflowSpawnResult, but no matching dataflow in `running_dataflows` map"); + } + }, + } + + // warn if event handling took too long -> the main loop should never be blocked for too long + let elapsed = start.elapsed(); + if elapsed > Duration::from_millis(100) { + tracing::warn!( + "Coordinator took {}ms for handling event: {event_kind}", + elapsed.as_millis() + ); } } @@ -711,8 +904,8 @@ async fn start_inner( Ok(()) } -async fn send_log_message(dataflow: &mut RunningDataflow, message: &LogMessage) { - for subscriber in &mut dataflow.log_subscribers { +async fn send_log_message(log_subscribers: &mut Vec, message: &LogMessage) { + for subscriber in log_subscribers.iter_mut() { let send_result = tokio::time::timeout(Duration::from_millis(100), subscriber.send_message(message)); @@ -720,7 +913,7 @@ async fn send_log_message(dataflow: &mut RunningDataflow, message: &LogMessage) subscriber.close(); } } - dataflow.log_subscribers.retain(|s| !s.is_closed()); + log_subscribers.retain(|s| !s.is_closed()); } fn dataflow_result( @@ -787,6 +980,15 @@ async fn send_heartbeat_message( .wrap_err("failed to send heartbeat message to daemon") } +struct RunningBuild { + errors: Vec, + build_result: CachedResult, + + log_subscribers: Vec, + + pending_build_results: BTreeSet, +} + struct RunningDataflow { name: Option, uuid: Uuid, @@ -797,9 +999,66 @@ struct RunningDataflow { exited_before_subscribe: Vec, nodes: BTreeMap, - reply_senders: Vec>>, + spawn_result: CachedResult, + stop_reply_senders: Vec>>, log_subscribers: Vec, + + pending_spawn_results: BTreeSet, +} + +pub enum CachedResult { + Pending { + result_senders: Vec>>, + }, + Cached { + result: eyre::Result, + }, +} + +impl Default for CachedResult { + fn default() -> Self { + Self::Pending { + result_senders: Vec::new(), + } + } +} + +impl CachedResult { + fn register( + &mut self, + reply_sender: tokio::sync::oneshot::Sender>, + ) { + match self { + CachedResult::Pending { result_senders } => result_senders.push(reply_sender), + CachedResult::Cached { result } => { + Self::send_result_to(result, reply_sender); + } + } + } + + fn set_result(&mut self, result: eyre::Result) { + match self { + CachedResult::Pending { result_senders } => { + for sender in result_senders.drain(..) { + Self::send_result_to(&result, sender); + } + *self = CachedResult::Cached { result }; + } + CachedResult::Cached { .. } => {} + } + } + + fn send_result_to( + result: &eyre::Result, + sender: oneshot::Sender>, + ) { + let result = match result { + Ok(r) => Ok(r.clone()), + Err(err) => Err(eyre!("{err:?}")), + }; + let _ = sender.send(result); + } } struct ArchivedDataflow { @@ -943,7 +1202,7 @@ async fn retrieve_logs( let machine_ids: Vec> = nodes .values() .filter(|node| node.id == node_id) - .map(|node| node.deploy.machine.clone()) + .map(|node| node.deploy.as_ref().and_then(|d| d.machine.clone())) .collect(); let machine_id = if let [machine_id] = &machine_ids[..] { @@ -992,9 +1251,127 @@ async fn retrieve_logs( reply_logs.map_err(|err| eyre!(err)) } +#[allow(clippy::too_many_arguments)] +#[tracing::instrument(skip(daemon_connections, clock))] +async fn build_dataflow( + build_id: BuildId, + session_id: SessionId, + dataflow: Descriptor, + git_sources: BTreeMap, + prev_git_sources: BTreeMap, + local_working_dir: Option, + clock: &HLC, + uv: bool, + daemon_connections: &mut DaemonConnections, +) -> eyre::Result { + let nodes = dataflow.resolve_aliases_and_set_defaults()?; + + let mut git_sources_by_daemon = git_sources + .into_iter() + .into_grouping_map_by(|(id, _)| { + nodes + .get(id) + .and_then(|n| n.deploy.as_ref().and_then(|d| d.machine.as_ref())) + }) + .collect(); + let mut prev_git_sources_by_daemon = prev_git_sources + .into_iter() + .into_grouping_map_by(|(id, _)| { + nodes + .get(id) + .and_then(|n| n.deploy.as_ref().and_then(|d| d.machine.as_ref())) + }) + .collect(); + + let nodes_by_daemon = nodes + .values() + .into_group_map_by(|n| n.deploy.as_ref().and_then(|d| d.machine.as_ref())); + + let mut daemons = BTreeSet::new(); + for (machine, nodes_on_machine) in &nodes_by_daemon { + let nodes_on_machine = nodes_on_machine.iter().map(|n| n.id.clone()).collect(); + tracing::debug!( + "Running dataflow build `{build_id}` on machine `{machine:?}` (nodes: {nodes_on_machine:?})" + ); + + let build_command = BuildDataflowNodes { + build_id, + session_id, + local_working_dir: local_working_dir.clone(), + git_sources: git_sources_by_daemon.remove(machine).unwrap_or_default(), + prev_git_sources: prev_git_sources_by_daemon + .remove(machine) + .unwrap_or_default(), + dataflow_descriptor: dataflow.clone(), + nodes_on_machine, + uv, + }; + let message = serde_json::to_vec(&Timestamped { + inner: DaemonCoordinatorEvent::Build(build_command), + timestamp: clock.new_timestamp(), + })?; + + let daemon_id = + build_dataflow_on_machine(daemon_connections, machine.map(|s| s.as_str()), &message) + .await + .wrap_err_with(|| format!("failed to build dataflow on machine `{machine:?}`"))?; + daemons.insert(daemon_id); + } + + tracing::info!("successfully triggered dataflow build `{build_id}`",); + + Ok(RunningBuild { + errors: Vec::new(), + build_result: CachedResult::default(), + log_subscribers: Vec::new(), + pending_build_results: daemons, + }) +} + +async fn build_dataflow_on_machine( + daemon_connections: &mut DaemonConnections, + machine: Option<&str>, + message: &[u8], +) -> Result { + let daemon_id = match machine { + Some(machine) => daemon_connections + .get_matching_daemon_id(machine) + .wrap_err_with(|| format!("no matching daemon for machine id {machine:?}"))? + .clone(), + None => daemon_connections + .unnamed() + .next() + .wrap_err("no unnamed daemon connections")? + .clone(), + }; + + let daemon_connection = daemon_connections + .get_mut(&daemon_id) + .wrap_err_with(|| format!("no daemon connection for daemon `{daemon_id}`"))?; + tcp_send(&mut daemon_connection.stream, message) + .await + .wrap_err("failed to send build message to daemon")?; + + let reply_raw = tcp_receive(&mut daemon_connection.stream) + .await + .wrap_err("failed to receive build reply from daemon")?; + match serde_json::from_slice(&reply_raw) + .wrap_err("failed to deserialize build reply from daemon")? + { + DaemonCoordinatorReply::TriggerBuildResult(result) => result + .map_err(|e| eyre!(e)) + .wrap_err("daemon returned an error")?, + _ => bail!("unexpected reply"), + } + Ok(daemon_id) +} + +#[allow(clippy::too_many_arguments)] async fn start_dataflow( + build_id: Option, + session_id: SessionId, dataflow: Descriptor, - working_dir: PathBuf, + local_working_dir: Option, name: Option, daemon_connections: &mut DaemonConnections, clock: &HLC, @@ -1004,7 +1381,16 @@ async fn start_dataflow( uuid, daemons, nodes, - } = spawn_dataflow(dataflow, working_dir, daemon_connections, clock, uv).await?; + } = spawn_dataflow( + build_id, + session_id, + dataflow, + local_working_dir, + daemon_connections, + clock, + uv, + ) + .await?; Ok(RunningDataflow { uuid, name, @@ -1014,10 +1400,12 @@ async fn start_dataflow( BTreeSet::new() }, exited_before_subscribe: Default::default(), - daemons, + daemons: daemons.clone(), nodes, - reply_senders: Vec::new(), + spawn_result: CachedResult::default(), + stop_reply_senders: Vec::new(), log_subscribers: Vec::new(), + pending_spawn_results: daemons, }) } @@ -1092,6 +1480,16 @@ pub enum Event { DaemonExit { daemon_id: dora_message::common::DaemonId, }, + DataflowBuildResult { + build_id: BuildId, + daemon_id: DaemonId, + result: eyre::Result<()>, + }, + DataflowSpawnResult { + dataflow_id: uuid::Uuid, + daemon_id: DaemonId, + result: eyre::Result<()>, + }, } impl Event { @@ -1103,6 +1501,23 @@ impl Event { _ => true, } } + + fn kind(&self) -> &'static str { + match self { + Event::NewDaemonConnection(_) => "NewDaemonConnection", + Event::DaemonConnectError(_) => "DaemonConnectError", + Event::DaemonHeartbeat { .. } => "DaemonHeartbeat", + Event::Dataflow { .. } => "Dataflow", + Event::Control(_) => "Control", + Event::Daemon(_) => "Daemon", + Event::DaemonHeartbeatInterval => "DaemonHeartbeatInterval", + Event::CtrlC => "CtrlC", + Event::Log(_) => "Log", + Event::DaemonExit { .. } => "DaemonExit", + Event::DataflowBuildResult { .. } => "DataflowBuildResult", + Event::DataflowSpawnResult { .. } => "DataflowSpawnResult", + } + } } #[derive(Debug)] diff --git a/binaries/coordinator/src/listener.rs b/binaries/coordinator/src/listener.rs index 6c666082..ab7e3b9d 100644 --- a/binaries/coordinator/src/listener.rs +++ b/binaries/coordinator/src/listener.rs @@ -112,6 +112,29 @@ pub async fn handle_connection( break; } } + DaemonEvent::BuildResult { build_id, result } => { + let event = Event::DataflowBuildResult { + build_id, + daemon_id, + result: result.map_err(|err| eyre::eyre!(err)), + }; + if events_tx.send(event).await.is_err() { + break; + } + } + DaemonEvent::SpawnResult { + dataflow_id, + result, + } => { + let event = Event::DataflowSpawnResult { + dataflow_id, + daemon_id, + result: result.map_err(|err| eyre::eyre!(err)), + }; + if events_tx.send(event).await.is_err() { + break; + } + } }, }; } diff --git a/binaries/coordinator/src/log_subscriber.rs b/binaries/coordinator/src/log_subscriber.rs index cb602d47..e5006616 100644 --- a/binaries/coordinator/src/log_subscriber.rs +++ b/binaries/coordinator/src/log_subscriber.rs @@ -17,9 +17,15 @@ impl LogSubscriber { } pub async fn send_message(&mut self, message: &LogMessage) -> eyre::Result<()> { - if message.level > self.level { - return Ok(()); + match message.level { + dora_core::build::LogLevelOrStdout::LogLevel(level) => { + if level > self.level { + return Ok(()); + } + } + dora_core::build::LogLevelOrStdout::Stdout => {} } + let message = serde_json::to_vec(&message)?; let connection = self.connection.as_mut().context("connection is closed")?; tcp_send(connection, &message) diff --git a/binaries/coordinator/src/run/mod.rs b/binaries/coordinator/src/run/mod.rs index f6f88e83..9edcabd3 100644 --- a/binaries/coordinator/src/run/mod.rs +++ b/binaries/coordinator/src/run/mod.rs @@ -10,6 +10,7 @@ use dora_message::{ daemon_to_coordinator::DaemonCoordinatorReply, descriptor::{Descriptor, ResolvedNode}, id::NodeId, + BuildId, SessionId, }; use eyre::{bail, eyre, ContextCompat, WrapErr}; use itertools::Itertools; @@ -21,8 +22,10 @@ use uuid::{NoContext, Timestamp, Uuid}; #[tracing::instrument(skip(daemon_connections, clock))] pub(super) async fn spawn_dataflow( + build_id: Option, + session_id: SessionId, dataflow: Descriptor, - working_dir: PathBuf, + local_working_dir: Option, daemon_connections: &mut DaemonConnections, clock: &HLC, uv: bool, @@ -30,7 +33,9 @@ pub(super) async fn spawn_dataflow( let nodes = dataflow.resolve_aliases_and_set_defaults()?; let uuid = Uuid::new_v7(Timestamp::now(NoContext)); - let nodes_by_daemon = nodes.values().into_group_map_by(|n| &n.deploy.machine); + let nodes_by_daemon = nodes + .values() + .into_group_map_by(|n| n.deploy.as_ref().and_then(|d| d.machine.as_ref())); let mut daemons = BTreeSet::new(); for (machine, nodes_on_machine) in &nodes_by_daemon { @@ -40,8 +45,10 @@ pub(super) async fn spawn_dataflow( ); let spawn_command = SpawnDataflowNodes { + build_id, + session_id, dataflow_id: uuid, - working_dir: working_dir.clone(), + local_working_dir: local_working_dir.clone(), nodes: nodes.clone(), dataflow_descriptor: dataflow.clone(), spawn_nodes, @@ -52,13 +59,14 @@ pub(super) async fn spawn_dataflow( timestamp: clock.new_timestamp(), })?; - let daemon_id = spawn_dataflow_on_machine(daemon_connections, machine.as_deref(), &message) - .await - .wrap_err_with(|| format!("failed to spawn dataflow on machine `{machine:?}`"))?; + let daemon_id = + spawn_dataflow_on_machine(daemon_connections, machine.map(|m| m.as_str()), &message) + .await + .wrap_err_with(|| format!("failed to spawn dataflow on machine `{machine:?}`"))?; daemons.insert(daemon_id); } - tracing::info!("successfully spawned dataflow `{uuid}`"); + tracing::info!("successfully triggered dataflow spawn `{uuid}`",); Ok(SpawnedDataflow { uuid, @@ -90,13 +98,14 @@ async fn spawn_dataflow_on_machine( tcp_send(&mut daemon_connection.stream, message) .await .wrap_err("failed to send spawn message to daemon")?; + let reply_raw = tcp_receive(&mut daemon_connection.stream) .await .wrap_err("failed to receive spawn reply from daemon")?; match serde_json::from_slice(&reply_raw) .wrap_err("failed to deserialize spawn reply from daemon")? { - DaemonCoordinatorReply::SpawnResult(result) => result + DaemonCoordinatorReply::TriggerSpawnResult(result) => result .map_err(|e| eyre!(e)) .wrap_err("daemon returned an error")?, _ => bail!("unexpected reply"), diff --git a/binaries/daemon/Cargo.toml b/binaries/daemon/Cargo.toml index ca29d9b5..423dba86 100644 --- a/binaries/daemon/Cargo.toml +++ b/binaries/daemon/Cargo.toml @@ -24,14 +24,14 @@ tracing = "0.1.36" tracing-opentelemetry = { version = "0.18.0", optional = true } futures-concurrency = "7.1.0" serde_json = "1.0.86" -dora-core = { workspace = true } +dora-core = { workspace = true, features = ["build"] } flume = "0.10.14" dora-download = { workspace = true } dora-tracing = { workspace = true, optional = true } dora-arrow-convert = { workspace = true } dora-node-api = { workspace = true } dora-message = { workspace = true } -serde_yaml = "0.8.23" +serde_yaml = { workspace = true } uuid = { version = "1.7", features = ["v7"] } futures = "0.3.25" shared-memory-server = { workspace = true } @@ -44,3 +44,7 @@ sysinfo = "0.30.11" crossbeam = "0.8.4" crossbeam-skiplist = "0.1.3" zenoh = "1.1.1" +url = "2.5.4" +git2 = { workspace = true } +dunce = "1.0.5" +itertools = "0.14" diff --git a/binaries/daemon/src/lib.rs b/binaries/daemon/src/lib.rs index e309e066..d23dd2b8 100644 --- a/binaries/daemon/src/lib.rs +++ b/binaries/daemon/src/lib.rs @@ -2,6 +2,7 @@ use aligned_vec::{AVec, ConstAlign}; use coordinator::CoordinatorEvent; use crossbeam::queue::ArrayQueue; use dora_core::{ + build::{self, BuildInfo, GitManager, PrevGitSource}, config::{DataId, Input, InputMapping, NodeId, NodeRunConfig, OperatorId}, descriptor::{ read_as_descriptor, CoreNodeKind, Descriptor, DescriptorExt, ResolvedNode, RuntimeNode, @@ -12,18 +13,20 @@ use dora_core::{ }; use dora_message::{ common::{ - DaemonId, DataMessage, DropToken, LogLevel, NodeError, NodeErrorCause, NodeExitStatus, + DaemonId, DataMessage, DropToken, GitSource, LogLevel, NodeError, NodeErrorCause, + NodeExitStatus, }, coordinator_to_cli::DataflowResult, - coordinator_to_daemon::{DaemonCoordinatorEvent, SpawnDataflowNodes}, + coordinator_to_daemon::{BuildDataflowNodes, DaemonCoordinatorEvent, SpawnDataflowNodes}, daemon_to_coordinator::{ CoordinatorRequest, DaemonCoordinatorReply, DaemonEvent, DataflowDaemonResult, }, daemon_to_daemon::InterDaemonEvent, daemon_to_node::{DaemonReply, NodeConfig, NodeDropEvent, NodeEvent}, + descriptor::NodeSource, metadata::{self, ArrowTypeInfo}, node_to_daemon::{DynamicNodeEvent, Timestamped}, - DataflowId, + BuildId, DataflowId, SessionId, }; use dora_node_api::{arrow::datatypes::DataType, Parameter}; use eyre::{bail, eyre, Context, ContextCompat, Result}; @@ -34,8 +37,11 @@ use log::{DaemonLogger, DataflowLogger, Logger}; use pending::PendingNodes; use shared_memory_server::ShmemConf; use socket_stream_utils::socket_stream_send; +use spawn::Spawner; use std::{ collections::{BTreeMap, BTreeSet, HashMap}, + env::current_dir, + future::Future, net::SocketAddr, path::{Path, PathBuf}, pin::pin, @@ -57,6 +63,9 @@ use tokio_stream::{wrappers::ReceiverStream, Stream, StreamExt}; use tracing::{error, warn}; use uuid::{NoContext, Timestamp, Uuid}; +pub use flume; +pub use log::LogDestination; + mod coordinator; mod local_listener; mod log; @@ -97,10 +106,20 @@ pub struct Daemon { remote_daemon_events_tx: Option>>>, logger: DaemonLogger, + + sessions: BTreeMap, + builds: BTreeMap, + git_manager: GitManager, } type DaemonRunResult = BTreeMap>>; +struct NodeBuildTask { + node_id: NodeId, + dynamic_node: bool, + task: F, +} + impl Daemon { pub async fn run( coordinator_addr: SocketAddr, @@ -130,6 +149,20 @@ impl Daemon { future::Either::Right((events, _)) => events?, } }; + + let log_destination = { + // additional connection for logging + let stream = TcpStream::connect(coordinator_addr) + .await + .wrap_err("failed to connect log to dora-coordinator")?; + stream + .set_nodelay(true) + .wrap_err("failed to set TCP_NODELAY")?; + LogDestination::Coordinator { + coordinator_connection: stream, + } + }; + Self::run_general( (ReceiverStream::new(ctrlc_events), incoming_events).merge(), Some(coordinator_addr), @@ -137,12 +170,21 @@ impl Daemon { None, clock, Some(remote_daemon_events_tx), + Default::default(), + log_destination, ) .await .map(|_| ()) } - pub async fn run_dataflow(dataflow_path: &Path, uv: bool) -> eyre::Result { + pub async fn run_dataflow( + dataflow_path: &Path, + build_id: Option, + local_build: Option, + session_id: SessionId, + uv: bool, + log_destination: LogDestination, + ) -> eyre::Result { let working_dir = dataflow_path .canonicalize() .context("failed to canonicalize dataflow path")? @@ -151,13 +193,24 @@ impl Daemon { .to_owned(); let descriptor = read_as_descriptor(dataflow_path).await?; + if let Some(node) = descriptor.nodes.iter().find(|n| n.deploy.is_some()) { + eyre::bail!( + "node {} has a `deploy` section, which is not supported in `dora run`\n\n + Instead, you need to spawn a `dora coordinator` and one or more `dora daemon` + instances and then use `dora start`.", + node.id + ) + } + descriptor.check(&working_dir)?; let nodes = descriptor.resolve_aliases_and_set_defaults()?; let dataflow_id = Uuid::new_v7(Timestamp::now(NoContext)); let spawn_command = SpawnDataflowNodes { + build_id, + session_id, dataflow_id, - working_dir, + local_working_dir: Some(working_dir), spawn_nodes: nodes.keys().cloned().collect(), nodes, dataflow_descriptor: descriptor, @@ -192,13 +245,24 @@ impl Daemon { Some(exit_when_done), clock.clone(), None, + if let Some(local_build) = local_build { + let Some(build_id) = build_id else { + bail!("no build_id, but local_build set") + }; + let mut builds = BTreeMap::new(); + builds.insert(build_id, local_build); + builds + } else { + Default::default() + }, + log_destination, ); let spawn_result = reply_rx .map_err(|err| eyre!("failed to receive spawn result: {err}")) .and_then(|r| async { match r { - Some(DaemonCoordinatorReply::SpawnResult(result)) => { + Some(DaemonCoordinatorReply::TriggerSpawnResult(result)) => { result.map_err(|err| eyre!(err)) } _ => Err(eyre!("unexpected spawn reply")), @@ -216,6 +280,7 @@ impl Daemon { }) } + #[allow(clippy::too_many_arguments)] async fn run_general( external_events: impl Stream> + Unpin, coordinator_addr: Option, @@ -223,6 +288,8 @@ impl Daemon { exit_when_done: Option>, clock: Arc, remote_daemon_events_tx: Option>>>, + builds: BTreeMap, + log_destination: LogDestination, ) -> eyre::Result { let coordinator_connection = match coordinator_addr { Some(addr) => { @@ -237,20 +304,6 @@ impl Daemon { None => None, }; - // additional connection for logging - let logger_coordinator_connection = match coordinator_addr { - Some(addr) => { - let stream = TcpStream::connect(addr) - .await - .wrap_err("failed to connect log to dora-coordinator")?; - stream - .set_nodelay(true) - .wrap_err("failed to set TCP_NODELAY")?; - Some(stream) - } - None => None, - }; - let zenoh_session = match std::env::var(zenoh::Config::DEFAULT_CONFIG_PATH_ENV) { Ok(path) => { let zenoh_config = zenoh::Config::from_file(&path) @@ -347,7 +400,7 @@ impl Daemon { let (dora_events_tx, dora_events_rx) = mpsc::channel(5); let daemon = Self { logger: Logger { - coordinator_connection: logger_coordinator_connection, + destination: log_destination, daemon_id: daemon_id.clone(), clock: clock.clone(), } @@ -364,6 +417,9 @@ impl Daemon { clock, zenoh_session, remote_daemon_events_tx, + git_manager: Default::default(), + builds, + sessions: Default::default(), }; let dora_events = ReceiverStream::new(dora_events_rx); @@ -392,6 +448,10 @@ impl Daemon { tracing::warn!("failed to update HLC with incoming event timestamp: {err}"); } + // used below for checking the duration of event handling + let start = Instant::now(); + let event_kind = inner.kind(); + match inner { Event::Coordinator(CoordinatorEvent { event, reply_tx }) => { let status = self.handle_coordinator_event(event, reply_tx).await?; @@ -409,10 +469,7 @@ impl Daemon { node_id, event, } => self.handle_node_event(event, dataflow, node_id).await?, - Event::Dora(event) => match self.handle_dora_event(event).await? { - RunStatus::Continue => {} - RunStatus::Exit => break, - }, + Event::Dora(event) => self.handle_dora_event(event).await?, Event::DynamicNode(event) => self.handle_dynamic_node_event(event).await?, Event::HeartbeatInterval => { if let Some(connection) = &mut self.coordinator_connection { @@ -457,6 +514,105 @@ impl Daemon { Event::DaemonError(err) => { tracing::error!("Daemon error: {err:?}"); } + Event::SpawnNodeResult { + dataflow_id, + node_id, + dynamic_node, + result, + } => match result { + Ok(running_node) => { + if let Some(dataflow) = self.running.get_mut(&dataflow_id) { + dataflow.running_nodes.insert(node_id, running_node); + } else { + tracing::error!("failed to handle SpawnNodeResult: no running dataflow with ID {dataflow_id}"); + } + } + Err(error) => { + self.dataflow_node_results + .entry(dataflow_id) + .or_default() + .insert(node_id.clone(), Err(error)); + self.handle_node_stop(dataflow_id, &node_id, dynamic_node) + .await?; + } + }, + Event::BuildDataflowResult { + build_id, + session_id, + result, + } => { + let (build_info, result) = match result { + Ok(build_info) => (Some(build_info), Ok(())), + Err(err) => (None, Err(err)), + }; + if let Some(build_info) = build_info { + self.builds.insert(build_id, build_info); + if let Some(old_build_id) = self.sessions.insert(session_id, build_id) { + self.builds.remove(&old_build_id); + } + } + if let Some(connection) = &mut self.coordinator_connection { + let msg = serde_json::to_vec(&Timestamped { + inner: CoordinatorRequest::Event { + daemon_id: self.daemon_id.clone(), + event: DaemonEvent::BuildResult { + build_id, + result: result.map_err(|err| format!("{err:?}")), + }, + }, + timestamp: self.clock.new_timestamp(), + })?; + socket_stream_send(connection, &msg).await.wrap_err( + "failed to send BuildDataflowResult message to dora-coordinator", + )?; + } + } + Event::SpawnDataflowResult { + dataflow_id, + result, + } => { + if let Some(connection) = &mut self.coordinator_connection { + let msg = serde_json::to_vec(&Timestamped { + inner: CoordinatorRequest::Event { + daemon_id: self.daemon_id.clone(), + event: DaemonEvent::SpawnResult { + dataflow_id, + result: result.map_err(|err| format!("{err:?}")), + }, + }, + timestamp: self.clock.new_timestamp(), + })?; + socket_stream_send(connection, &msg).await.wrap_err( + "failed to send SpawnDataflowResult message to dora-coordinator", + )?; + } + } + Event::NodeStopped { + dataflow_id, + node_id, + } => { + if let Some(exit_when_done) = &mut self.exit_when_done { + exit_when_done.remove(&(dataflow_id, node_id)); + if exit_when_done.is_empty() { + tracing::info!( + "exiting daemon because all required dataflows are finished" + ); + break; + } + } + if self.exit_when_all_finished && self.running.is_empty() { + break; + } + } + } + + // warn if event handling took too long -> the main loop should never be blocked for too long + let elapsed = start.elapsed(); + if elapsed > Duration::from_millis(100) { + tracing::warn!( + "Daemon took {}ms for handling event: {event_kind}", + elapsed.as_millis() + ); } } @@ -482,9 +638,73 @@ impl Daemon { reply_tx: Sender>, ) -> eyre::Result { let status = match event { + DaemonCoordinatorEvent::Build(BuildDataflowNodes { + build_id, + session_id, + local_working_dir, + git_sources, + prev_git_sources, + dataflow_descriptor, + nodes_on_machine, + uv, + }) => { + match dataflow_descriptor.communication.remote { + dora_core::config::RemoteCommunicationConfig::Tcp => {} + } + + let base_working_dir = self.base_working_dir(local_working_dir, session_id)?; + + let result = self + .build_dataflow( + build_id, + session_id, + base_working_dir, + git_sources, + prev_git_sources, + dataflow_descriptor, + nodes_on_machine, + uv, + ) + .await; + let (trigger_result, result_task) = match result { + Ok(result_task) => (Ok(()), Some(result_task)), + Err(err) => (Err(format!("{err:?}")), None), + }; + let reply = DaemonCoordinatorReply::TriggerBuildResult(trigger_result); + let _ = reply_tx.send(Some(reply)).map_err(|_| { + error!("could not send `TriggerBuildResult` reply from daemon to coordinator") + }); + + let result_tx = self.events_tx.clone(); + let clock = self.clock.clone(); + if let Some(result_task) = result_task { + tokio::spawn(async move { + let message = Timestamped { + inner: Event::BuildDataflowResult { + build_id, + session_id, + result: result_task.await, + }, + timestamp: clock.new_timestamp(), + }; + let _ = result_tx + .send(message) + .map_err(|_| { + error!( + "could not send `BuildResult` reply from daemon to coordinator" + ) + }) + .await; + }); + } + + RunStatus::Continue + } DaemonCoordinatorEvent::Spawn(SpawnDataflowNodes { + build_id, + session_id, dataflow_id, - working_dir, + local_working_dir, nodes, dataflow_descriptor, spawn_nodes, @@ -494,31 +714,50 @@ impl Daemon { dora_core::config::RemoteCommunicationConfig::Tcp => {} } - // Use the working directory if it exists, otherwise use the working directory where the daemon is spawned - let working_dir = if working_dir.exists() { - working_dir - } else { - std::env::current_dir().wrap_err("failed to get current working dir")? - }; + let base_working_dir = self.base_working_dir(local_working_dir, session_id)?; let result = self .spawn_dataflow( + build_id, dataflow_id, - working_dir, + base_working_dir, nodes, dataflow_descriptor, spawn_nodes, uv, ) .await; - if let Err(err) = &result { - tracing::error!("{err:?}"); - } - let reply = - DaemonCoordinatorReply::SpawnResult(result.map_err(|err| format!("{err:?}"))); + let (trigger_result, result_task) = match result { + Ok(result_task) => (Ok(()), Some(result_task)), + Err(err) => (Err(format!("{err:?}")), None), + }; + let reply = DaemonCoordinatorReply::TriggerSpawnResult(trigger_result); let _ = reply_tx.send(Some(reply)).map_err(|_| { - error!("could not send `SpawnResult` reply from daemon to coordinator") + error!("could not send `TriggerSpawnResult` reply from daemon to coordinator") }); + + let result_tx = self.events_tx.clone(); + let clock = self.clock.clone(); + if let Some(result_task) = result_task { + tokio::spawn(async move { + let message = Timestamped { + inner: Event::SpawnDataflowResult { + dataflow_id, + result: result_task.await, + }, + timestamp: clock.new_timestamp(), + }; + let _ = result_tx + .send(message) + .map_err(|_| { + error!( + "could not send `SpawnResult` reply from daemon to coordinator" + ) + }) + .await; + }); + } + RunStatus::Continue } DaemonCoordinatorEvent::AllNodesReady { @@ -750,21 +989,125 @@ impl Daemon { } } + #[allow(clippy::too_many_arguments)] + async fn build_dataflow( + &mut self, + build_id: BuildId, + session_id: SessionId, + base_working_dir: PathBuf, + git_sources: BTreeMap, + prev_git_sources: BTreeMap, + dataflow_descriptor: Descriptor, + local_nodes: BTreeSet, + uv: bool, + ) -> eyre::Result>> { + let builder = build::Builder { + session_id, + base_working_dir, + uv, + }; + self.git_manager.clear_planned_builds(session_id); + + let nodes = dataflow_descriptor.resolve_aliases_and_set_defaults()?; + + let mut tasks = Vec::new(); + + // build nodes + for node in nodes.into_values().filter(|n| local_nodes.contains(&n.id)) { + let dynamic_node = node.kind.dynamic(); + + let node_id = node.id.clone(); + let mut logger = self.logger.for_node_build(build_id, node_id.clone()); + logger.log(LogLevel::Info, "building").await; + 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 logger_cloned = logger + .try_clone_impl() + .await + .wrap_err("failed to clone logger")?; + + let mut builder = builder.clone(); + if let Some(node_working_dir) = + node.deploy.as_ref().and_then(|d| d.working_dir.as_deref()) + { + builder.base_working_dir = builder.base_working_dir.join(node_working_dir); + } + + match builder + .build_node( + node, + git_source, + prev_git, + logger_cloned, + &mut self.git_manager, + ) + .await + .wrap_err_with(|| format!("failed to build node `{node_id}`")) + { + Ok(result) => { + tasks.push(NodeBuildTask { + node_id, + task: result, + dynamic_node, + }); + } + Err(err) => { + logger.log(LogLevel::Error, format!("{err:?}")).await; + return Err(err); + } + } + } + + let task = async move { + let mut info = BuildInfo { + node_working_dirs: Default::default(), + }; + for task in tasks { + let NodeBuildTask { + node_id, + dynamic_node, + task, + } = task; + 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) + }; + + Ok(task) + } + + #[allow(clippy::too_many_arguments)] async fn spawn_dataflow( &mut self, - dataflow_id: uuid::Uuid, - working_dir: PathBuf, + build_id: Option, + dataflow_id: DataflowId, + base_working_dir: PathBuf, nodes: BTreeMap, dataflow_descriptor: Descriptor, spawn_nodes: BTreeSet, uv: bool, - ) -> eyre::Result<()> { - let mut logger = self.logger.for_dataflow(dataflow_id); + ) -> eyre::Result>> { + let mut logger = self + .logger + .for_dataflow(dataflow_id) + .try_clone() + .await + .context("failed to clone logger")?; let dataflow = RunningDataflow::new(dataflow_id, self.daemon_id.clone(), &dataflow_descriptor); let dataflow = match self.running.entry(dataflow_id) { std::collections::hash_map::Entry::Vacant(entry) => { - self.working_dir.insert(dataflow_id, working_dir.clone()); + self.working_dir + .insert(dataflow_id, base_working_dir.clone()); entry.insert(dataflow) } std::collections::hash_map::Entry::Occupied(_) => { @@ -774,6 +1117,11 @@ impl Daemon { let mut stopped = Vec::new(); + let node_working_dirs = build_id + .and_then(|build_id| self.builds.get(&build_id)) + .map(|info| info.node_working_dirs.clone()) + .unwrap_or_default(); + // calculate info about mappings for node in nodes.values() { let local = spawn_nodes.contains(&node.id); @@ -810,12 +1158,23 @@ impl Daemon { } } + let spawner = Spawner { + dataflow_id, + daemon_tx: self.events_tx.clone(), + dataflow_descriptor, + clock: self.clock.clone(), + uv, + }; + + let mut tasks = Vec::new(); + // spawn nodes and set up subscriptions for node in nodes.into_values() { let mut logger = logger.reborrow().for_node(node.id.clone()); let local = spawn_nodes.contains(&node.id); if local { - if node.kind.dynamic() { + let dynamic_node = node.kind.dynamic(); + if dynamic_node { dataflow.dynamic_nodes.insert(node.id.clone()); } else { dataflow.pending_nodes.insert(node.id.clone()); @@ -830,22 +1189,28 @@ impl Daemon { logger .log(LogLevel::Info, Some("daemon".into()), "spawning") .await; - match spawn::spawn_node( - dataflow_id, - &working_dir, - node, - self.events_tx.clone(), - dataflow_descriptor.clone(), - self.clock.clone(), - node_stderr_most_recent, - uv, - &mut logger, - ) - .await - .wrap_err_with(|| format!("failed to spawn node `{node_id}`")) + let node_working_dir = node_working_dirs + .get(&node_id) + .cloned() + .or_else(|| { + node.deploy + .as_ref() + .and_then(|d| d.working_dir.as_ref().map(|d| base_working_dir.join(d))) + }) + .unwrap_or(base_working_dir.clone()) + .clone(); + match spawner + .clone() + .spawn_node(node, node_working_dir, node_stderr_most_recent, &mut logger) + .await + .wrap_err_with(|| format!("failed to spawn node `{node_id}`")) { - Ok(running_node) => { - dataflow.running_nodes.insert(node_id, running_node); + Ok(result) => { + tasks.push(NodeBuildTask { + node_id, + task: result, + dynamic_node, + }); } Err(err) => { logger @@ -858,13 +1223,11 @@ impl Daemon { node_id.clone(), Err(NodeError { timestamp: self.clock.new_timestamp(), - cause: NodeErrorCause::Other { - stderr: format!("spawn failed: {err:?}"), - }, + cause: NodeErrorCause::FailedToSpawn(format!("{err:?}")), exit_status: NodeExitStatus::Unknown, }), ); - stopped.push(node_id.clone()); + stopped.push((node_id.clone(), dynamic_node)); } } } else { @@ -922,11 +1285,133 @@ impl Daemon { } } } - for node_id in stopped { - self.handle_node_stop(dataflow_id, &node_id).await?; + for (node_id, dynamic) in stopped { + self.handle_node_stop(dataflow_id, &node_id, dynamic) + .await?; } - Ok(()) + let spawn_result = Self::spawn_prepared_nodes( + dataflow_id, + logger, + tasks, + self.events_tx.clone(), + self.clock.clone(), + ); + + Ok(spawn_result) + } + + async fn spawn_prepared_nodes( + dataflow_id: Uuid, + mut logger: DataflowLogger<'_>, + tasks: Vec>>>, + events_tx: mpsc::Sender>, + clock: Arc, + ) -> eyre::Result<()> { + let node_result = |node_id, dynamic_node, result| Timestamped { + inner: Event::SpawnNodeResult { + dataflow_id, + node_id, + dynamic_node, + result, + }, + timestamp: clock.new_timestamp(), + }; + let mut failed_to_prepare = None; + let mut prepared_nodes = Vec::new(); + for task in tasks { + let NodeBuildTask { + node_id, + dynamic_node, + task, + } = task; + match task.await { + Ok(node) => prepared_nodes.push(node), + Err(err) => { + if failed_to_prepare.is_none() { + failed_to_prepare = Some(node_id.clone()); + } + let node_err: NodeError = NodeError { + timestamp: clock.new_timestamp(), + cause: NodeErrorCause::FailedToSpawn(format!( + "preparing for spawn failed: {err:?}" + )), + exit_status: NodeExitStatus::Unknown, + }; + let send_result = events_tx + .send(node_result(node_id, dynamic_node, Err(node_err))) + .await; + if send_result.is_err() { + tracing::error!("failed to send SpawnNodeResult to main daemon task") + } + } + } + } + + // once all nodes are prepared, do the actual spawning + if let Some(failed_node) = failed_to_prepare { + // don't spawn any nodes when an error occurred before + for node in prepared_nodes { + let err = NodeError { + timestamp: clock.new_timestamp(), + cause: NodeErrorCause::Cascading { + caused_by_node: failed_node.clone(), + }, + exit_status: NodeExitStatus::Unknown, + }; + let send_result = events_tx + .send(node_result( + node.node_id().clone(), + node.dynamic(), + Err(err), + )) + .await; + if send_result.is_err() { + tracing::error!("failed to send SpawnNodeResult to main daemon task") + } + } + Err(eyre!("failed to prepare node {failed_node}")) + } else { + let mut spawn_result = Ok(()); + + logger + .log( + LogLevel::Info, + None, + Some("dora daemon".into()), + "finished building nodes, spawning...", + ) + .await; + + // spawn the nodes + for node in prepared_nodes { + let node_id = node.node_id().clone(); + let dynamic_node = node.dynamic(); + let mut logger = logger.reborrow().for_node(node_id.clone()); + let result = node.spawn(&mut logger).await; + let node_spawn_result = match result { + Ok(node) => Ok(node), + Err(err) => { + let node_err = NodeError { + timestamp: clock.new_timestamp(), + cause: NodeErrorCause::FailedToSpawn(format!("spawn failed: {err:?}")), + exit_status: NodeExitStatus::Unknown, + }; + if spawn_result.is_ok() { + spawn_result = Err(err.wrap_err(format!("failed to spawn {node_id}"))); + } + Err(node_err) + } + }; + let send_result = events_tx + .send(node_result(node_id, dynamic_node, node_spawn_result)) + .await; + if send_result.is_err() { + tracing::error!("failed to send SpawnNodeResult to main daemon task") + } + } + spawn_result + } } async fn handle_dynamic_node_event( @@ -946,7 +1431,7 @@ impl Daemon { let node_config = match number_node_id { 2.. => Err(format!( - "multiple dataflows contains dynamic node id {node_id}. \ + "multiple dataflows contain dynamic node id {node_id}. \ Please only have one running dataflow with the specified \ node id if you want to use dynamic node", )), @@ -958,7 +1443,9 @@ impl Daemon { let node_config = dataflow .running_nodes .get(&node_id) - .context("no node with ID `{node_id}` within the given dataflow")? + .with_context(|| { + format!("no node with ID `{node_id}` within the given dataflow") + })? .node_config .clone(); if !node_config.dynamic { @@ -974,7 +1461,7 @@ impl Daemon { "failed to get dynamic node config within given dataflow: {err}" ) }), - 0 => Err("no node with ID `{node_id}`".to_string()), + 0 => Err(format!("no node with ID `{node_id}`")), }; let reply = DaemonReply::NodeConfig { @@ -1348,11 +1835,49 @@ impl Daemon { Ok(()) } - async fn handle_node_stop(&mut self, dataflow_id: Uuid, node_id: &NodeId) -> eyre::Result<()> { + async fn handle_node_stop( + &mut self, + dataflow_id: Uuid, + node_id: &NodeId, + dynamic_node: bool, + ) -> eyre::Result<()> { + let result = self + .handle_node_stop_inner(dataflow_id, node_id, dynamic_node) + .await; + let _ = self + .events_tx + .send(Timestamped { + inner: Event::NodeStopped { + dataflow_id, + node_id: node_id.clone(), + }, + timestamp: self.clock.new_timestamp(), + }) + .await; + result + } + + async fn handle_node_stop_inner( + &mut self, + dataflow_id: Uuid, + node_id: &NodeId, + dynamic_node: bool, + ) -> eyre::Result<()> { let mut logger = self.logger.for_dataflow(dataflow_id); - let dataflow = self.running.get_mut(&dataflow_id).wrap_err_with(|| { - format!("failed to get downstream nodes: no running dataflow with ID `{dataflow_id}`") - })?; + let dataflow = match self.running.get_mut(&dataflow_id) { + Some(dataflow) => dataflow, + None if dynamic_node => { + // The dataflow might be done already as we don't wait for dynamic nodes. In this + // case, we don't need to do anything to handle the node stop. + tracing::debug!( + "dynamic node {dataflow_id}/{node_id} stopped after dataflow was done" + ); + return Ok(()); + } + None => eyre::bail!( + "failed to get downstream nodes: no running dataflow with ID `{dataflow_id}`" + ), + }; dataflow .pending_nodes @@ -1374,10 +1899,11 @@ impl Daemon { if let Some(mut pid) = dataflow.running_nodes.remove(node_id).and_then(|n| n.pid) { pid.mark_as_stopped() } - if dataflow - .running_nodes - .iter() - .all(|(_id, n)| n.node_config.dynamic) + if !dataflow.pending_nodes.local_nodes_pending() + && dataflow + .running_nodes + .iter() + .all(|(_id, n)| n.node_config.dynamic) { let result = DataflowDaemonResult { timestamp: self.clock.new_timestamp(), @@ -1388,6 +1914,13 @@ impl Daemon { .clone(), }; + self.git_manager + .clones_in_use + .values_mut() + .for_each(|dataflows| { + dataflows.remove(&dataflow_id); + }); + logger .log( LogLevel::Info, @@ -1417,7 +1950,7 @@ impl Daemon { Ok(()) } - async fn handle_dora_event(&mut self, event: DoraEvent) -> eyre::Result { + async fn handle_dora_event(&mut self, event: DoraEvent) -> eyre::Result<()> { match event { DoraEvent::Timer { dataflow_id, @@ -1426,11 +1959,11 @@ impl Daemon { } => { let Some(dataflow) = self.running.get_mut(&dataflow_id) else { tracing::warn!("Timer event for unknown dataflow `{dataflow_id}`"); - return Ok(RunStatus::Continue); + return Ok(()); }; let Some(subscribers) = dataflow.timers.get(&interval) else { - return Ok(RunStatus::Continue); + return Ok(()); }; let mut closed = Vec::new(); @@ -1467,7 +2000,7 @@ impl Daemon { } => { let Some(dataflow) = self.running.get_mut(&dataflow_id) else { tracing::warn!("Logs event for unknown dataflow `{dataflow_id}`"); - return Ok(RunStatus::Continue); + return Ok(()); }; let Some(subscribers) = dataflow.mappings.get(&output_id) else { @@ -1476,7 +2009,7 @@ impl Daemon { output_id, dataflow.mappings ); - return Ok(RunStatus::Continue); + return Ok(()); }; let mut closed = Vec::new(); @@ -1509,6 +2042,7 @@ impl Daemon { DoraEvent::SpawnedNodeResult { dataflow_id, node_id, + dynamic_node, exit_status, } => { let mut logger = self @@ -1596,23 +2130,39 @@ impl Daemon { .or_default() .insert(node_id.clone(), node_result); - self.handle_node_stop(dataflow_id, &node_id).await?; + self.handle_node_stop(dataflow_id, &node_id, dynamic_node) + .await?; + } + } + Ok(()) + } - if let Some(exit_when_done) = &mut self.exit_when_done { - exit_when_done.remove(&(dataflow_id, node_id)); - if exit_when_done.is_empty() { - tracing::info!( - "exiting daemon because all required dataflows are finished" - ); - return Ok(RunStatus::Exit); - } - } - if self.exit_when_all_finished && self.running.is_empty() { - return Ok(RunStatus::Exit); + fn base_working_dir( + &self, + local_working_dir: Option, + session_id: SessionId, + ) -> eyre::Result { + match local_working_dir { + Some(working_dir) => { + // check that working directory exists + if working_dir.exists() { + Ok(working_dir) + } else { + bail!( + "working directory does not exist: {}", + working_dir.display(), + ) } } + None => { + // 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(RunStatus::Continue) } } @@ -1777,7 +2327,7 @@ fn close_input( } #[derive(Debug)] -struct RunningNode { +pub struct RunningNode { pid: Option, node_config: NodeConfig, } @@ -2082,6 +2632,25 @@ pub enum Event { CtrlC, SecondCtrlC, DaemonError(eyre::Report), + SpawnNodeResult { + dataflow_id: DataflowId, + node_id: NodeId, + dynamic_node: bool, + result: Result, + }, + BuildDataflowResult { + build_id: BuildId, + session_id: SessionId, + result: eyre::Result, + }, + SpawnDataflowResult { + dataflow_id: Uuid, + result: eyre::Result<()>, + }, + NodeStopped { + dataflow_id: Uuid, + node_id: NodeId, + }, } impl From for Event { @@ -2090,6 +2659,26 @@ impl From for Event { } } +impl Event { + pub fn kind(&self) -> &'static str { + match self { + Event::Node { .. } => "Node", + Event::Coordinator(_) => "Coordinator", + Event::Daemon(_) => "Daemon", + Event::Dora(_) => "Dora", + Event::DynamicNode(_) => "DynamicNode", + Event::HeartbeatInterval => "HeartbeatInterval", + Event::CtrlC => "CtrlC", + Event::SecondCtrlC => "SecondCtrlC", + Event::DaemonError(_) => "DaemonError", + Event::SpawnNodeResult { .. } => "SpawnNodeResult", + Event::BuildDataflowResult { .. } => "BuildDataflowResult", + Event::SpawnDataflowResult { .. } => "SpawnDataflowResult", + Event::NodeStopped { .. } => "NodeStopped", + } + } +} + #[derive(Debug)] pub enum DaemonNodeEvent { OutputsDone { @@ -2136,6 +2725,7 @@ pub enum DoraEvent { SpawnedNodeResult { dataflow_id: DataflowId, node_id: NodeId, + dynamic_node: bool, exit_status: NodeExitStatus, }, } @@ -2255,7 +2845,9 @@ impl CoreNodeKindExt for CoreNodeKind { fn dynamic(&self) -> bool { match self { CoreNodeKind::Runtime(_n) => false, - CoreNodeKind::Custom(n) => n.source == DYNAMIC_SOURCE, + CoreNodeKind::Custom(n) => { + matches!(&n.source, NodeSource::Local) && n.path == DYNAMIC_SOURCE + } } } } diff --git a/binaries/daemon/src/log.rs b/binaries/daemon/src/log.rs index c9e41334..283213c8 100644 --- a/binaries/daemon/src/log.rs +++ b/binaries/daemon/src/log.rs @@ -1,14 +1,21 @@ use std::{ + ops::{Deref, DerefMut}, path::{Path, PathBuf}, sync::Arc, }; -use dora_core::{config::NodeId, uhlc}; +use dora_core::{ + build::{BuildLogger, LogLevelOrStdout}, + config::NodeId, + uhlc, +}; use dora_message::{ common::{DaemonId, LogLevel, LogMessage, Timestamped}, daemon_to_coordinator::{CoordinatorRequest, DaemonEvent}, + BuildId, }; use eyre::Context; +use flume::Sender; use tokio::net::TcpStream; use uuid::Uuid; @@ -39,11 +46,18 @@ impl NodeLogger<'_> { .log(level, Some(self.node_id.clone()), target, message) .await } + + pub async fn try_clone(&self) -> eyre::Result> { + Ok(NodeLogger { + node_id: self.node_id.clone(), + logger: self.logger.try_clone().await?, + }) + } } pub struct DataflowLogger<'a> { dataflow_id: Uuid, - logger: &'a mut DaemonLogger, + logger: CowMut<'a, DaemonLogger>, } impl<'a> DataflowLogger<'a> { @@ -57,12 +71,12 @@ impl<'a> DataflowLogger<'a> { pub fn reborrow(&mut self) -> DataflowLogger { DataflowLogger { dataflow_id: self.dataflow_id, - logger: self.logger, + logger: CowMut::Borrowed(&mut self.logger), } } pub fn inner(&self) -> &DaemonLogger { - self.logger + &self.logger } pub async fn log( @@ -73,9 +87,64 @@ impl<'a> DataflowLogger<'a> { message: impl Into, ) { self.logger - .log(level, self.dataflow_id, node_id, target, message) + .log(level, Some(self.dataflow_id), node_id, target, message) .await } + + pub async fn try_clone(&self) -> eyre::Result> { + Ok(DataflowLogger { + dataflow_id: self.dataflow_id, + logger: CowMut::Owned(self.logger.try_clone().await?), + }) + } +} + +pub struct NodeBuildLogger<'a> { + build_id: BuildId, + node_id: NodeId, + logger: CowMut<'a, DaemonLogger>, +} + +impl NodeBuildLogger<'_> { + pub async fn log( + &mut self, + level: impl Into + Send, + message: impl Into, + ) { + self.logger + .log_build( + self.build_id, + level.into(), + None, + Some(self.node_id.clone()), + message, + ) + .await + } + + pub async fn try_clone_impl(&self) -> eyre::Result> { + Ok(NodeBuildLogger { + build_id: self.build_id, + node_id: self.node_id.clone(), + logger: CowMut::Owned(self.logger.try_clone().await?), + }) + } +} + +impl BuildLogger for NodeBuildLogger<'_> { + type Clone = NodeBuildLogger<'static>; + + fn log_message( + &mut self, + level: impl Into + Send, + message: impl Into + Send, + ) -> impl std::future::Future + Send { + self.log(level, message) + } + + fn try_clone(&self) -> impl std::future::Future> + Send { + self.try_clone_impl() + } } pub struct DaemonLogger { @@ -87,7 +156,15 @@ impl DaemonLogger { pub fn for_dataflow(&mut self, dataflow_id: Uuid) -> DataflowLogger { DataflowLogger { dataflow_id, - logger: self, + logger: CowMut::Borrowed(self), + } + } + + pub fn for_node_build(&mut self, build_id: BuildId, node_id: NodeId) -> NodeBuildLogger { + NodeBuildLogger { + build_id, + node_id, + logger: CowMut::Borrowed(self), } } @@ -98,15 +175,39 @@ impl DaemonLogger { pub async fn log( &mut self, level: LogLevel, - dataflow_id: Uuid, + dataflow_id: Option, node_id: Option, target: Option, message: impl Into, ) { let message = LogMessage { + build_id: None, daemon_id: Some(self.daemon_id.clone()), dataflow_id, node_id, + level: level.into(), + target, + module_path: None, + file: None, + line: None, + message: message.into(), + }; + self.logger.log(message).await + } + + pub async fn log_build( + &mut self, + build_id: BuildId, + level: LogLevelOrStdout, + target: Option, + node_id: Option, + message: impl Into, + ) { + let message = LogMessage { + build_id: Some(build_id), + daemon_id: Some(self.daemon_id.clone()), + dataflow_id: None, + node_id, level, target, module_path: None, @@ -120,10 +221,17 @@ impl DaemonLogger { pub(crate) fn daemon_id(&self) -> &DaemonId { &self.daemon_id } + + pub async fn try_clone(&self) -> eyre::Result { + Ok(Self { + daemon_id: self.daemon_id.clone(), + logger: self.logger.try_clone().await?, + }) + } } pub struct Logger { - pub(super) coordinator_connection: Option, + pub(super) destination: LogDestination, pub(super) daemon_id: DaemonId, pub(super) clock: Arc, } @@ -137,73 +245,179 @@ impl Logger { } pub async fn log(&mut self, message: LogMessage) { - if let Some(connection) = &mut self.coordinator_connection { - let msg = serde_json::to_vec(&Timestamped { - inner: CoordinatorRequest::Event { - daemon_id: self.daemon_id.clone(), - event: DaemonEvent::Log(message.clone()), - }, - timestamp: self.clock.new_timestamp(), - }) - .expect("failed to serialize log message"); - match socket_stream_send(connection, &msg) - .await - .wrap_err("failed to send log message to dora-coordinator") - { - Ok(()) => return, - Err(err) => tracing::warn!("{err:?}"), + match &mut self.destination { + LogDestination::Coordinator { + coordinator_connection, + } => { + let message = Timestamped { + inner: CoordinatorRequest::Event { + daemon_id: self.daemon_id.clone(), + event: DaemonEvent::Log(message.clone()), + }, + timestamp: self.clock.new_timestamp(), + }; + Self::log_to_coordinator(message, coordinator_connection).await } - } - - // log message using tracing if reporting to coordinator is not possible - match message.level { - LogLevel::Error => { - if let Some(node_id) = message.node_id { - tracing::error!("{}/{} errored:", message.dataflow_id.to_string(), node_id); - } - for line in message.message.lines() { - tracing::error!(" {}", line); - } + LogDestination::Channel { sender } => { + let _ = sender.send_async(message).await; } - LogLevel::Warn => { - if let Some(node_id) = message.node_id { - tracing::warn!("{}/{} warned:", message.dataflow_id.to_string(), node_id); - } - for line in message.message.lines() { - tracing::warn!(" {}", line); + LogDestination::Tracing => { + // log message using tracing if reporting to coordinator is not possible + match message.level { + LogLevelOrStdout::Stdout => { + tracing::info!( + build_id = ?message.build_id.map(|id| id.to_string()), + dataflow_id = ?message.dataflow_id.map(|id| id.to_string()), + node_id = ?message.node_id.map(|id| id.to_string()), + target = message.target, + module_path = message.module_path, + file = message.file, + line = message.line, + "{}", + Indent(&message.message) + ) + } + LogLevelOrStdout::LogLevel(level) => match level { + LogLevel::Error => { + tracing::error!( + build_id = ?message.build_id.map(|id| id.to_string()), + dataflow_id = ?message.dataflow_id.map(|id| id.to_string()), + node_id = ?message.node_id.map(|id| id.to_string()), + target = message.target, + module_path = message.module_path, + file = message.file, + line = message.line, + "{}", + Indent(&message.message) + ); + } + LogLevel::Warn => { + tracing::warn!( + build_id = ?message.build_id.map(|id| id.to_string()), + dataflow_id = ?message.dataflow_id.map(|id| id.to_string()), + node_id = ?message.node_id.map(|id| id.to_string()), + target = message.target, + module_path = message.module_path, + file = message.file, + line = message.line, + "{}", + Indent(&message.message) + ); + } + LogLevel::Info => { + tracing::info!( + build_id = ?message.build_id.map(|id| id.to_string()), + dataflow_id = ?message.dataflow_id.map(|id| id.to_string()), + node_id = ?message.node_id.map(|id| id.to_string()), + target = message.target, + module_path = message.module_path, + file = message.file, + line = message.line, + "{}", + Indent(&message.message) + ); + } + LogLevel::Debug => { + tracing::debug!( + build_id = ?message.build_id.map(|id| id.to_string()), + dataflow_id = ?message.dataflow_id.map(|id| id.to_string()), + node_id = ?message.node_id.map(|id| id.to_string()), + target = message.target, + module_path = message.module_path, + file = message.file, + line = message.line, + "{}", + Indent(&message.message) + ); + } + _ => {} + }, } } - LogLevel::Info => { - if let Some(node_id) = message.node_id { - tracing::info!("{}/{} info:", message.dataflow_id.to_string(), node_id); - } - - for line in message.message.lines() { - tracing::info!(" {}", line); - } - } - _ => {} } } pub async fn try_clone(&self) -> eyre::Result { - let coordinator_connection = match &self.coordinator_connection { - Some(c) => { - let addr = c + let destination = match &self.destination { + LogDestination::Coordinator { + coordinator_connection, + } => { + let addr = coordinator_connection .peer_addr() .context("failed to get coordinator peer addr")?; let new_connection = TcpStream::connect(addr) .await .context("failed to connect to coordinator during logger clone")?; - Some(new_connection) + LogDestination::Coordinator { + coordinator_connection: new_connection, + } } - None => None, + LogDestination::Channel { sender } => LogDestination::Channel { + sender: sender.clone(), + }, + LogDestination::Tracing => LogDestination::Tracing, }; Ok(Self { - coordinator_connection, + destination, daemon_id: self.daemon_id.clone(), clock: self.clock.clone(), }) } + + async fn log_to_coordinator( + message: Timestamped, + connection: &mut TcpStream, + ) { + let msg = serde_json::to_vec(&message).expect("failed to serialize log message"); + match socket_stream_send(connection, &msg) + .await + .wrap_err("failed to send log message to dora-coordinator") + { + Ok(()) => return, + Err(err) => tracing::warn!("{err:?}"), + } + } +} + +pub enum LogDestination { + Coordinator { coordinator_connection: TcpStream }, + Channel { sender: Sender }, + Tracing, +} + +enum CowMut<'a, T> { + Borrowed(&'a mut T), + Owned(T), +} + +impl Deref for CowMut<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + CowMut::Borrowed(v) => v, + CowMut::Owned(v) => v, + } + } +} + +impl DerefMut for CowMut<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + CowMut::Borrowed(v) => v, + CowMut::Owned(v) => v, + } + } +} + +struct Indent<'a>(&'a str); + +impl std::fmt::Display for Indent<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for line in self.0.lines() { + write!(f, " {}", line)?; + } + Ok(()) + } } diff --git a/binaries/daemon/src/pending.rs b/binaries/daemon/src/pending.rs index 89305d80..757a858d 100644 --- a/binaries/daemon/src/pending.rs +++ b/binaries/daemon/src/pending.rs @@ -59,6 +59,10 @@ impl PendingNodes { self.external_nodes = value; } + pub fn local_nodes_pending(&self) -> bool { + !self.local_nodes.is_empty() + } + pub async fn handle_node_subscription( &mut self, node_id: NodeId, diff --git a/binaries/daemon/src/spawn.rs b/binaries/daemon/src/spawn.rs index 9087a4ec..2ecc5c1a 100644 --- a/binaries/daemon/src/spawn.rs +++ b/binaries/daemon/src/spawn.rs @@ -20,6 +20,7 @@ use dora_message::{ common::{LogLevel, LogMessage}, daemon_to_coordinator::{DataMessage, NodeExitStatus, Timestamped}, daemon_to_node::{NodeConfig, RuntimeConfig}, + id::NodeId, DataflowId, }; use dora_node_api::{ @@ -29,6 +30,7 @@ use dora_node_api::{ }; use eyre::{bail, ContextCompat, WrapErr}; use std::{ + future::Future, path::{Path, PathBuf}, process::Stdio, sync::Arc, @@ -40,565 +42,671 @@ use tokio::{ }; use tracing::error; -/// clock is required for generating timestamps when dropping messages early because queue is full -pub async fn spawn_node( - dataflow_id: DataflowId, - working_dir: &Path, - node: ResolvedNode, - daemon_tx: mpsc::Sender>, - dataflow_descriptor: Descriptor, - clock: Arc, - node_stderr_most_recent: Arc>, - uv: bool, - logger: &mut NodeLogger<'_>, -) -> eyre::Result { - let node_id = node.id.clone(); - logger - .log( - LogLevel::Debug, - Some("daemon::spawner".into()), - "spawning node", +#[derive(Clone)] +pub struct Spawner { + pub dataflow_id: DataflowId, + pub daemon_tx: mpsc::Sender>, + pub dataflow_descriptor: Descriptor, + /// clock is required for generating timestamps when dropping messages early because queue is full + pub clock: Arc, + pub uv: bool, +} + +impl Spawner { + pub async fn spawn_node( + self, + node: ResolvedNode, + node_working_dir: PathBuf, + node_stderr_most_recent: Arc>, + logger: &mut NodeLogger<'_>, + ) -> eyre::Result>> { + let dataflow_id = self.dataflow_id; + let node_id = node.id.clone(); + logger + .log( + LogLevel::Debug, + Some("daemon::spawner".into()), + "spawning node", + ) + .await; + + let queue_sizes = node_inputs(&node) + .into_iter() + .map(|(k, v)| (k, v.queue_size.unwrap_or(10))) + .collect(); + let daemon_communication = spawn_listener_loop( + &dataflow_id, + &node_id, + &self.daemon_tx, + self.dataflow_descriptor.communication.local, + queue_sizes, + self.clock.clone(), ) - .await; - - let queue_sizes = node_inputs(&node) - .into_iter() - .map(|(k, v)| (k, v.queue_size.unwrap_or(10))) - .collect(); - let daemon_communication = spawn_listener_loop( - &dataflow_id, - &node_id, - &daemon_tx, - dataflow_descriptor.communication.local, - queue_sizes, - clock.clone(), - ) - .await?; - let send_stdout_to = node - .send_stdout_as() - .context("Could not resolve `send_stdout_as` configuration")?; - - let node_config = NodeConfig { - dataflow_id, - node_id: node_id.clone(), - run_config: node.kind.run_config(), - daemon_communication, - dataflow_descriptor, - dynamic: node.kind.dynamic(), - }; + .await?; - let mut child = match node.kind { - dora_core::descriptor::CoreNodeKind::Custom(n) => { - let mut command = match n.source.as_str() { - DYNAMIC_SOURCE => { - return Ok(RunningNode { - pid: None, - node_config, - }); - } - SHELL_SOURCE => { - if cfg!(target_os = "windows") { - let mut cmd = tokio::process::Command::new("cmd"); - cmd.args(["/C", &n.args.clone().unwrap_or_default()]); - cmd - } else { - let mut cmd = tokio::process::Command::new("sh"); - cmd.args(["-c", &n.args.clone().unwrap_or_default()]); - cmd - } - } - source => { - let resolved_path = if source_is_url(source) { - // try to download the shared library - let target_dir = Path::new("build"); - download_file(source, target_dir) - .await - .wrap_err("failed to download custom node")? - } else { - resolve_path(source, working_dir).wrap_err_with(|| { - format!("failed to resolve node source `{}`", source) - })? - }; + let node_config = NodeConfig { + dataflow_id, + node_id: node_id.clone(), + run_config: node.kind.run_config(), + daemon_communication, + dataflow_descriptor: serde_yaml::to_value(&self.dataflow_descriptor) + .context("failed to serialize dataflow descriptor to YAML")?, + dynamic: node.kind.dynamic(), + }; - // If extension is .py, use python to run the script - let mut cmd = match resolved_path.extension().map(|ext| ext.to_str()) { - Some(Some("py")) => { - let mut cmd = if uv { - let mut cmd = tokio::process::Command::new("uv"); - cmd.arg("run"); - cmd.arg("python"); - logger - .log( - LogLevel::Info, - Some("spawner".into()), - format!( - "spawning: uv run python -u {}", - resolved_path.display() - ), - ) - .await; - cmd - } else { - let python = get_python_path().wrap_err( - "Could not find python path when spawning custom node", - )?; - logger - .log( - LogLevel::Info, - Some("spawner".into()), - format!( - "spawning: {:?} -u {}", - &python, - resolved_path.display() - ), - ) - .await; - - tokio::process::Command::new(python) - }; - // Force python to always flush stdout/stderr buffer - cmd.arg("-u"); - cmd.arg(&resolved_path); - cmd + let mut logger = logger + .try_clone() + .await + .wrap_err("failed to clone logger")?; + let task = async move { + self.prepare_node_inner( + node, + node_working_dir, + &mut logger, + dataflow_id, + node_config, + node_stderr_most_recent, + ) + .await + }; + Ok(task) + } + + async fn prepare_node_inner( + self, + node: ResolvedNode, + node_working_dir: PathBuf, + logger: &mut NodeLogger<'_>, + dataflow_id: uuid::Uuid, + node_config: NodeConfig, + node_stderr_most_recent: Arc>, + ) -> eyre::Result { + let (command, error_msg) = match &node.kind { + dora_core::descriptor::CoreNodeKind::Custom(n) => { + let mut command = + path_spawn_command(&node_working_dir, self.uv, logger, n, true).await?; + + if let Some(command) = &mut command { + command.current_dir(&node_working_dir); + command.stdin(Stdio::null()); + + command.env( + "DORA_NODE_CONFIG", + serde_yaml::to_string(&node_config.clone()) + .wrap_err("failed to serialize node config")?, + ); + // Injecting the env variable defined in the `yaml` into + // the node runtime. + if let Some(envs) = &node.env { + for (key, value) in envs { + command.env(key, value.to_string()); } - _ => { - logger - .log( - LogLevel::Info, - Some("spawner".into()), - format!("spawning: {}", resolved_path.display()), - ) - .await; - if uv { - let mut cmd = tokio::process::Command::new("uv"); - cmd.arg("run"); - cmd.arg(&resolved_path); - cmd - } else { - tokio::process::Command::new(&resolved_path) - } + } + if let Some(envs) = &n.envs { + // node has some inner env variables -> add them too + for (key, value) in envs { + command.env(key, value.to_string()); } - }; - - if let Some(args) = &n.args { - cmd.args(args.split_ascii_whitespace()); } - cmd - } - }; - command.current_dir(working_dir); - command.stdin(Stdio::null()); - - command.env( - "DORA_NODE_CONFIG", - serde_yaml::to_string(&node_config.clone()) - .wrap_err("failed to serialize node config")?, - ); - // Injecting the env variable defined in the `yaml` into - // the node runtime. - if let Some(envs) = node.env { - for (key, value) in envs { - command.env(key, value.to_string()); - } - } - if let Some(envs) = n.envs { - // node has some inner env variables -> add them too - for (key, value) in envs { - command.env(key, value.to_string()); - } - } + // Set the process group to 0 to ensure that the spawned process does not exit immediately on CTRL-C + #[cfg(unix)] + command.process_group(0); - // Set the process group to 0 to ensure that the spawned process does not exit immediately on CTRL-C - #[cfg(unix)] - command.process_group(0); - - command.env("PYTHONUNBUFFERED", "1"); - command - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .wrap_err_with(move || { - format!( - "failed to run `{}` with args `{}`", - n.source, - n.args.as_deref().unwrap_or_default(), - ) - })? - } - dora_core::descriptor::CoreNodeKind::Runtime(n) => { - let python_operators: Vec<&OperatorDefinition> = n - .operators - .iter() - .filter(|x| matches!(x.config.source, OperatorSource::Python { .. })) - .collect(); - - let other_operators = n - .operators - .iter() - .any(|x| !matches!(x.config.source, OperatorSource::Python { .. })); - - let mut command = if !python_operators.is_empty() && !other_operators { - // Use python to spawn runtime if there is a python operator - - // TODO: Handle multi-operator runtime once sub-interpreter is supported - if python_operators.len() > 2 { - eyre::bail!( - "Runtime currently only support one Python Operator. + command.env("PYTHONUNBUFFERED", "1"); + command + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + }; + + let error_msg = format!( + "failed to run `{}` with args `{}`", + n.path, + n.args.as_deref().unwrap_or_default(), + ); + (command, error_msg) + } + dora_core::descriptor::CoreNodeKind::Runtime(n) => { + let python_operators: Vec<&OperatorDefinition> = n + .operators + .iter() + .filter(|x| matches!(x.config.source, OperatorSource::Python { .. })) + .collect(); + + let other_operators = n + .operators + .iter() + .any(|x| !matches!(x.config.source, OperatorSource::Python { .. })); + + let mut command = if !python_operators.is_empty() && !other_operators { + // Use python to spawn runtime if there is a python operator + + // TODO: Handle multi-operator runtime once sub-interpreter is supported + if python_operators.len() > 2 { + eyre::bail!( + "Runtime currently only support one Python Operator. This is because pyo4 sub-interpreter is not yet available. See: https://github.com/PyO4/pyo3/issues/576" - ); - } + ); + } - let python_operator = python_operators - .first() - .context("Runtime had no operators definition.")?; + let python_operator = python_operators + .first() + .context("Runtime had no operators definition.")?; - if let OperatorSource::Python(PythonSource { - source: _, - conda_env: Some(conda_env), - }) = &python_operator.config.source - { - let conda = which::which("conda").context( + if let OperatorSource::Python(PythonSource { + source: _, + conda_env: Some(conda_env), + }) = &python_operator.config.source + { + let conda = which::which("conda").context( "failed to find `conda`, yet a `conda_env` was defined. Make sure that `conda` is available.", - )?; - let mut command = tokio::process::Command::new(conda); - command.args([ - "run", - "-n", - conda_env, - "python", - "-uc", - format!("import dora; dora.start_runtime() # {}", node.id).as_str(), - ]); - command - } else { - let mut cmd = if uv { - let mut cmd = tokio::process::Command::new("uv"); - cmd.arg("run"); - cmd.arg("python"); - tracing::info!( + )?; + let mut command = tokio::process::Command::new(conda); + command.args([ + "run", + "-n", + conda_env, + "python", + "-uc", + format!("import dora; dora.start_runtime() # {}", node.id).as_str(), + ]); + Some(command) + } else { + let mut cmd = if self.uv { + let mut cmd = tokio::process::Command::new("uv"); + cmd.arg("run"); + cmd.arg("python"); + tracing::info!( "spawning: uv run python -uc import dora; dora.start_runtime() # {}", node.id ); - cmd - } else { + cmd + } else { + let python = get_python_path() + .wrap_err("Could not find python path when spawning custom node")?; + tracing::info!( + "spawning: python -uc import dora; dora.start_runtime() # {}", + node.id + ); + + tokio::process::Command::new(python) + }; + // Force python to always flush stdout/stderr buffer + cmd.args([ + "-uc", + format!("import dora; dora.start_runtime() # {}", node.id).as_str(), + ]); + Some(cmd) + } + } else if python_operators.is_empty() && other_operators { + let current_exe = std::env::current_exe() + .wrap_err("failed to get current executable path")?; + let mut file_name = current_exe.clone(); + file_name.set_extension(""); + let file_name = file_name + .file_name() + .and_then(|s| s.to_str()) + .context("failed to get file name from current executable")?; + + // Check if the current executable is a python binary meaning that dora is installed within the python environment + if file_name.ends_with("python") || file_name.ends_with("python3") { + // Use the current executable to spawn runtime let python = get_python_path() .wrap_err("Could not find python path when spawning custom node")?; + let mut cmd = tokio::process::Command::new(python); + tracing::info!( "spawning: python -uc import dora; dora.start_runtime() # {}", node.id ); - tokio::process::Command::new(python) - }; - // Force python to always flush stdout/stderr buffer - cmd.args([ - "-uc", - format!("import dora; dora.start_runtime() # {}", node.id).as_str(), - ]); - cmd - } - } else if python_operators.is_empty() && other_operators { - let current_exe = - std::env::current_exe().wrap_err("failed to get current executable path")?; - let mut file_name = current_exe.clone(); - file_name.set_extension(""); - let file_name = file_name - .file_name() - .and_then(|s| s.to_str()) - .context("failed to get file name from current executable")?; - - // Check if the current executable is a python binary meaning that dora is installed within the python environment - if file_name.ends_with("python") || file_name.ends_with("python3") { - // Use the current executable to spawn runtime - let python = get_python_path() - .wrap_err("Could not find python path when spawning custom node")?; - let mut cmd = tokio::process::Command::new(python); - - tracing::info!( - "spawning: python -uc import dora; dora.start_runtime() # {}", - node.id - ); - - cmd.args([ - "-uc", - format!("import dora; dora.start_runtime() # {}", node.id).as_str(), - ]); - cmd + cmd.args([ + "-uc", + format!("import dora; dora.start_runtime() # {}", node.id).as_str(), + ]); + Some(cmd) + } else { + let mut cmd = tokio::process::Command::new( + std::env::current_exe() + .wrap_err("failed to get current executable path")?, + ); + cmd.arg("runtime"); + Some(cmd) + } } else { - let mut cmd = tokio::process::Command::new( - std::env::current_exe() - .wrap_err("failed to get current executable path")?, - ); - cmd.arg("runtime"); - cmd - } - } else { - bail!( - "Cannot spawn runtime with both Python and non-Python operators. \ + bail!( + "Cannot spawn runtime with both Python and non-Python operators. \ Please use a single operator or ensure that all operators are Python-based." - ); - }; + ); + }; - command.current_dir(working_dir); + let runtime_config = RuntimeConfig { + node: node_config.clone(), + operators: n.operators.clone(), + }; - let runtime_config = RuntimeConfig { - node: node_config.clone(), - operators: n.operators, - }; - command.env( - "DORA_RUNTIME_CONFIG", - serde_yaml::to_string(&runtime_config) - .wrap_err("failed to serialize runtime config")?, - ); - // Injecting the env variable defined in the `yaml` into - // the node runtime. - if let Some(envs) = node.env { - for (key, value) in envs { - command.env(key, value.to_string()); - } - } - // Set the process group to 0 to ensure that the spawned process does not exit immediately on CTRL-C - #[cfg(unix)] - command.process_group(0); - - command - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .wrap_err(format!( + if let Some(command) = &mut command { + command.current_dir(&node_working_dir); + + command.env( + "DORA_RUNTIME_CONFIG", + serde_yaml::to_string(&runtime_config) + .wrap_err("failed to serialize runtime config")?, + ); + // Injecting the env variable defined in the `yaml` into + // the node runtime. + if let Some(envs) = &node.env { + for (key, value) in envs { + command.env(key, value.to_string()); + } + } + // Set the process group to 0 to ensure that the spawned process does not exit immediately on CTRL-C + #[cfg(unix)] + command.process_group(0); + + command + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + }; + let error_msg = format!( "failed to run runtime {}/{}", runtime_config.node.dataflow_id, runtime_config.node.node_id - ))? - } - }; + ); + (command, error_msg) + } + }; + Ok(PreparedNode { + command, + spawn_error_msg: error_msg, + node_working_dir, + dataflow_id, + node, + node_config, + clock: self.clock, + daemon_tx: self.daemon_tx, + node_stderr_most_recent, + }) + } +} - let pid = crate::ProcessId::new(child.id().context( - "Could not get the pid for the just spawned node and indicate that there is an error", - )?); - logger - .log( - LogLevel::Debug, - Some("spawner".into()), - format!("spawned node with pid {pid:?}"), - ) - .await; +pub struct PreparedNode { + command: Option, + spawn_error_msg: String, + node_working_dir: PathBuf, + dataflow_id: DataflowId, + node: ResolvedNode, + node_config: NodeConfig, + clock: Arc, + daemon_tx: mpsc::Sender>, + node_stderr_most_recent: Arc>, +} + +impl PreparedNode { + pub fn node_id(&self) -> &NodeId { + &self.node.id + } - let dataflow_dir: PathBuf = working_dir.join("out").join(dataflow_id.to_string()); - if !dataflow_dir.exists() { - std::fs::create_dir_all(&dataflow_dir).context("could not create dataflow_dir")?; + pub fn dynamic(&self) -> bool { + self.node.kind.dynamic() } - let (tx, mut rx) = mpsc::channel(10); - let mut file = File::create(log::log_path(working_dir, &dataflow_id, &node_id)) + + pub async fn spawn(mut self, logger: &mut NodeLogger<'_>) -> eyre::Result { + let mut child = match &mut self.command { + Some(command) => command.spawn().wrap_err(self.spawn_error_msg)?, + None => { + return Ok(RunningNode { + pid: None, + node_config: self.node_config, + }) + } + }; + + let pid = crate::ProcessId::new(child.id().context( + "Could not get the pid for the just spawned node and indicate that there is an error", + )?); + logger + .log( + LogLevel::Debug, + Some("spawner".into()), + format!("spawned node with pid {pid:?}"), + ) + .await; + + let dataflow_dir: PathBuf = self + .node_working_dir + .join("out") + .join(self.dataflow_id.to_string()); + if !dataflow_dir.exists() { + std::fs::create_dir_all(&dataflow_dir).context("could not create dataflow_dir")?; + } + let (tx, mut rx) = mpsc::channel(10); + let mut file = File::create(log::log_path( + &self.node_working_dir, + &self.dataflow_id, + &self.node.id, + )) .await .expect("Failed to create log file"); - let mut child_stdout = - tokio::io::BufReader::new(child.stdout.take().expect("failed to take stdout")); - let running_node = RunningNode { - pid: Some(pid), - node_config, - }; - let stdout_tx = tx.clone(); - let node_id = node.id.clone(); - // Stdout listener stream - tokio::spawn(async move { - let mut buffer = String::new(); - let mut finished = false; - while !finished { - let mut raw = Vec::new(); - finished = match child_stdout - .read_until(b'\n', &mut raw) - .await - .wrap_err_with(|| format!("failed to read stdout line from spawned node {node_id}")) - { - Ok(0) => true, - Ok(_) => false, - Err(err) => { - tracing::warn!("{err:?}"); - false + let mut child_stdout = + tokio::io::BufReader::new(child.stdout.take().expect("failed to take stdout")); + let running_node = RunningNode { + pid: Some(pid), + node_config: self.node_config, + }; + let stdout_tx = tx.clone(); + let node_id = self.node.id.clone(); + // Stdout listener stream + tokio::spawn(async move { + let mut buffer = String::new(); + let mut finished = false; + while !finished { + let mut raw = Vec::new(); + finished = match child_stdout + .read_until(b'\n', &mut raw) + .await + .wrap_err_with(|| { + format!("failed to read stdout line from spawned node {node_id}") + }) { + Ok(0) => true, + Ok(_) => false, + Err(err) => { + tracing::warn!("{err:?}"); + false + } + }; + + match String::from_utf8(raw) { + Ok(s) => buffer.push_str(&s), + Err(err) => { + let lossy = String::from_utf8_lossy(err.as_bytes()); + tracing::warn!( + "stdout not valid UTF-8 string (node {node_id}): {}: {lossy}", + err.utf8_error() + ); + buffer.push_str(&lossy) + } + }; + + 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; + } } - }; - match String::from_utf8(raw) { - Ok(s) => buffer.push_str(&s), - Err(err) => { - let lossy = String::from_utf8_lossy(err.as_bytes()); - tracing::warn!( - "stdout not valid UTF-8 string (node {node_id}): {}: {lossy}", - err.utf8_error() - ); - buffer.push_str(&lossy) + // send the buffered lines + let lines = std::mem::take(&mut buffer); + let sent = stdout_tx.send(lines.clone()).await; + if sent.is_err() { + println!("Could not log: {lines}"); } + } + }); + + let mut child_stderr = + tokio::io::BufReader::new(child.stderr.take().expect("failed to take stderr")); + + // Stderr listener stream + let stderr_tx = tx.clone(); + let node_id = self.node.id.clone(); + let uhlc = self.clock.clone(); + let daemon_tx_log = self.daemon_tx.clone(); + tokio::spawn(async move { + let mut buffer = String::new(); + let mut finished = false; + while !finished { + let mut raw = Vec::new(); + finished = match child_stderr + .read_until(b'\n', &mut raw) + .await + .wrap_err_with(|| { + format!("failed to read stderr line from spawned node {node_id}") + }) { + Ok(0) => true, + Ok(_) => false, + Err(err) => { + tracing::warn!("{err:?}"); + true + } + }; + + let new = match String::from_utf8(raw) { + Ok(s) => s, + Err(err) => { + let lossy = String::from_utf8_lossy(err.as_bytes()); + tracing::warn!( + "stderr not valid UTF-8 string (node {node_id}): {}: {lossy}", + err.utf8_error() + ); + lossy.into_owned() + } + }; + + buffer.push_str(&new); + + self.node_stderr_most_recent.force_push(new); + + // send the buffered lines + let lines = std::mem::take(&mut buffer); + let sent = stderr_tx.send(lines.clone()).await; + if sent.is_err() { + println!("Could not log: {lines}"); + } + } + }); + + let node_id = self.node.id.clone(); + let dynamic_node = self.node.kind.dynamic(); + let (log_finish_tx, log_finish_rx) = oneshot::channel(); + let clock = self.clock.clone(); + let daemon_tx = self.daemon_tx.clone(); + let dataflow_id = self.dataflow_id; + tokio::spawn(async move { + let exit_status = NodeExitStatus::from(child.wait().await); + let _ = log_finish_rx.await; + let event = DoraEvent::SpawnedNodeResult { + dataflow_id, + node_id, + exit_status, + dynamic_node, + } + .into(); + let event = Timestamped { + inner: event, + timestamp: clock.new_timestamp(), }; + let _ = daemon_tx.send(event).await; + }); + + let node_id = self.node.id.clone(); + let daemon_id = logger.inner().inner().daemon_id().clone(); + let mut cloned_logger = logger + .inner() + .inner() + .inner() + .try_clone() + .await + .context("failed to clone logger")?; + + let send_stdout_to = self + .node + .send_stdout_as() + .context("Could not resolve `send_stdout_as` configuration")?; + + // Log to file stream. + tokio::spawn(async move { + while let Some(message) = rx.recv().await { + // If log is an output, we're sending the logs to the dataflow + if let Some(stdout_output_name) = &send_stdout_to { + // Convert logs to DataMessage + let array = message.as_str().into_arrow(); + + let array: ArrayData = array.into(); + let total_len = required_data_size(&array); + let mut sample: AVec> = + AVec::__from_elem(128, 0, total_len); + + let type_info = copy_array_into_sample(&mut sample, &array); + + let metadata = Metadata::new(uhlc.new_timestamp(), type_info); + let output_id = OutputId( + node_id.clone(), + DataId::from(stdout_output_name.to_string()), + ); + let event = DoraEvent::Logs { + dataflow_id, + output_id, + metadata, + message: DataMessage::Vec(sample), + } + .into(); + let event = Timestamped { + inner: event, + timestamp: uhlc.new_timestamp(), + }; + let _ = daemon_tx_log.send(event).await; + } - 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; + let _ = file + .write_all(message.as_bytes()) + .await + .map_err(|err| error!("Could not log {message} to file due to {err}")); + let formatted = message.lines().fold(String::default(), |mut output, line| { + output.push_str(line); + output + }); + if std::env::var("DORA_QUIET").is_err() { + cloned_logger + .log(LogMessage { + daemon_id: Some(daemon_id.clone()), + dataflow_id: Some(dataflow_id), + build_id: None, + level: dora_core::build::LogLevelOrStdout::Stdout, + node_id: Some(node_id.clone()), + target: None, + message: formatted, + file: None, + line: None, + module_path: None, + }) + .await; } + // Make sure that all data has been synced to disk. + let _ = file + .sync_all() + .await + .map_err(|err| error!("Could not sync logs to file due to {err}")); } + let _ = log_finish_tx + .send(()) + .map_err(|_| error!("Could not inform that log file thread finished")); + }); + Ok(running_node) + } +} - // send the buffered lines - let lines = std::mem::take(&mut buffer); - let sent = stdout_tx.send(lines.clone()).await; - if sent.is_err() { - println!("Could not log: {lines}"); +async fn path_spawn_command( + working_dir: &Path, + uv: bool, + logger: &mut NodeLogger<'_>, + node: &dora_core::descriptor::CustomNode, + permit_url: bool, +) -> eyre::Result> { + let cmd = match node.path.as_str() { + DYNAMIC_SOURCE => return Ok(None), + SHELL_SOURCE => { + if cfg!(target_os = "windows") { + let mut cmd = tokio::process::Command::new("cmd"); + cmd.args(["/C", &node.args.clone().unwrap_or_default()]); + cmd + } else { + let mut cmd = tokio::process::Command::new("sh"); + cmd.args(["-c", &node.args.clone().unwrap_or_default()]); + cmd } } - }); - - let mut child_stderr = - tokio::io::BufReader::new(child.stderr.take().expect("failed to take stderr")); - - // Stderr listener stream - let stderr_tx = tx.clone(); - let node_id = node.id.clone(); - let uhlc = clock.clone(); - let daemon_tx_log = daemon_tx.clone(); - tokio::spawn(async move { - let mut buffer = String::new(); - let mut finished = false; - while !finished { - let mut raw = Vec::new(); - finished = match child_stderr - .read_until(b'\n', &mut raw) - .await - .wrap_err_with(|| format!("failed to read stderr line from spawned node {node_id}")) - { - Ok(0) => true, - Ok(_) => false, - Err(err) => { - tracing::warn!("{err:?}"); - true + source => { + let resolved_path = if source_is_url(source) { + if !permit_url { + eyre::bail!("URL paths are not supported in this case"); } + // try to download the shared library + let target_dir = Path::new("build"); + download_file(source, target_dir) + .await + .wrap_err("failed to download custom node")? + } else { + resolve_path(source, working_dir) + .wrap_err_with(|| format!("failed to resolve node source `{}`", source))? }; - let new = match String::from_utf8(raw) { - Ok(s) => s, - Err(err) => { - let lossy = String::from_utf8_lossy(err.as_bytes()); - tracing::warn!( - "stderr not valid UTF-8 string (node {node_id}): {}: {lossy}", - err.utf8_error() - ); - lossy.into_owned() + // If extension is .py, use python to run the script + let mut cmd = match resolved_path.extension().map(|ext| ext.to_str()) { + Some(Some("py")) => { + let mut cmd = if uv { + let mut cmd = tokio::process::Command::new("uv"); + cmd.arg("run"); + cmd.arg("python"); + logger + .log( + LogLevel::Info, + Some("spawner".into()), + format!("spawning: uv run python -u {}", resolved_path.display()), + ) + .await; + cmd + } else { + let python = get_python_path() + .wrap_err("Could not find python path when spawning custom node")?; + logger + .log( + LogLevel::Info, + Some("spawner".into()), + format!("spawning: {:?} -u {}", &python, resolved_path.display()), + ) + .await; + + tokio::process::Command::new(python) + }; + // Force python to always flush stdout/stderr buffer + cmd.arg("-u"); + cmd.arg(&resolved_path); + cmd + } + _ => { + logger + .log( + LogLevel::Info, + Some("spawner".into()), + format!("spawning: {}", resolved_path.display()), + ) + .await; + if uv { + let mut cmd = tokio::process::Command::new("uv"); + cmd.arg("run"); + cmd.arg(&resolved_path); + cmd + } else { + tokio::process::Command::new(&resolved_path) + } } }; - buffer.push_str(&new); - - node_stderr_most_recent.force_push(new); - - // send the buffered lines - let lines = std::mem::take(&mut buffer); - let sent = stderr_tx.send(lines.clone()).await; - if sent.is_err() { - println!("Could not log: {lines}"); + if let Some(args) = &node.args { + cmd.args(args.split_ascii_whitespace()); } + cmd } - }); - - let node_id = node.id.clone(); - let (log_finish_tx, log_finish_rx) = oneshot::channel(); - tokio::spawn(async move { - let exit_status = NodeExitStatus::from(child.wait().await); - let _ = log_finish_rx.await; - let event = DoraEvent::SpawnedNodeResult { - dataflow_id, - node_id, - exit_status, - } - .into(); - let event = Timestamped { - inner: event, - timestamp: clock.new_timestamp(), - }; - let _ = daemon_tx.send(event).await; - }); - - let node_id = node.id.clone(); - let daemon_id = logger.inner().inner().daemon_id().clone(); - let mut cloned_logger = logger - .inner() - .inner() - .inner() - .try_clone() - .await - .context("failed to clone logger")?; - // Log to file stream. - tokio::spawn(async move { - while let Some(message) = rx.recv().await { - // If log is an output, we're sending the logs to the dataflow - if let Some(stdout_output_name) = &send_stdout_to { - // Convert logs to DataMessage - let array = message.into_arrow(); - - let array: ArrayData = array.into(); - let total_len = required_data_size(&array); - let mut sample: AVec> = AVec::__from_elem(128, 0, total_len); - - let type_info = copy_array_into_sample(&mut sample, &array); - - let metadata = Metadata::new(uhlc.new_timestamp(), type_info); - let output_id = OutputId( - node_id.clone(), - DataId::from(stdout_output_name.to_string()), - ); - let event = DoraEvent::Logs { - dataflow_id, - output_id, - metadata, - message: DataMessage::Vec(sample), - } - .into(); - let event = Timestamped { - inner: event, - timestamp: uhlc.new_timestamp(), - }; - let _ = daemon_tx_log.send(event).await; - } + }; - let _ = file - .write_all(message.as_bytes()) - .await - .map_err(|err| error!("Could not log {message} to file due to {err}")); - let formatted = message.lines().fold(String::default(), |mut output, line| { - output.push_str(line); - output - }); - if std::env::var("DORA_QUIET").is_err() { - cloned_logger - .log(LogMessage { - daemon_id: Some(daemon_id.clone()), - dataflow_id, - level: LogLevel::Info, - node_id: Some(node_id.clone()), - target: Some("stdout".into()), - message: formatted, - file: None, - line: None, - module_path: None, - }) - .await; - } - // Make sure that all data has been synced to disk. - let _ = file - .sync_all() - .await - .map_err(|err| error!("Could not sync logs to file due to {err}")); - } - let _ = log_finish_tx - .send(()) - .map_err(|_| error!("Could not inform that log file thread finished")); - }); - Ok(running_node) + Ok(Some(cmd)) } diff --git a/binaries/runtime/Cargo.toml b/binaries/runtime/Cargo.toml index 270ba264..73e6c615 100644 --- a/binaries/runtime/Cargo.toml +++ b/binaries/runtime/Cargo.toml @@ -21,7 +21,7 @@ eyre = "0.6.8" futures = "0.3.21" futures-concurrency = "7.1.0" libloading = "0.7.3" -serde_yaml = "0.8.23" +serde_yaml = { workspace = true } tokio = { version = "1.24.2", features = ["full"] } tokio-stream = "0.1.8" # pyo3-abi3 flag allow simpler linking. See: https://pyo3.rs/v0.13.2/building_and_distribution.html diff --git a/binaries/runtime/src/lib.rs b/binaries/runtime/src/lib.rs index ea949bf4..7a42142a 100644 --- a/binaries/runtime/src/lib.rs +++ b/binaries/runtime/src/lib.rs @@ -43,7 +43,8 @@ pub fn main() -> eyre::Result<()> { .wrap_err("failed to set up tracing subscriber")?; } - let dataflow_descriptor = config.dataflow_descriptor.clone(); + let dataflow_descriptor = serde_yaml::from_value(config.dataflow_descriptor.clone()) + .context("failed to parse dataflow descriptor")?; let operator_definition = if operators.is_empty() { bail!("no operators"); @@ -232,10 +233,10 @@ async fn run( } } } - RuntimeEvent::Event(Event::Stop) => { + RuntimeEvent::Event(Event::Stop(cause)) => { // forward stop event to all operators and close the event channels for (_, channel) in operator_channels.drain() { - let _ = channel.send_async(Event::Stop).await; + let _ = channel.send_async(Event::Stop(cause.clone())).await; } } RuntimeEvent::Event(Event::Reload { diff --git a/binaries/runtime/src/operator/shared_lib.rs b/binaries/runtime/src/operator/shared_lib.rs index b7795470..e2920a2a 100644 --- a/binaries/runtime/src/operator/shared_lib.rs +++ b/binaries/runtime/src/operator/shared_lib.rs @@ -182,7 +182,7 @@ impl<'lib> SharedLibraryOperator<'lib> { } let mut operator_event = match event { - Event::Stop => dora_operator_api_types::RawEvent { + Event::Stop(_) => dora_operator_api_types::RawEvent { input: None, input_closed: None, stop: true, diff --git a/examples/c++-arrow-dataflow/run.rs b/examples/c++-arrow-dataflow/run.rs index 399a73b1..a77c4d78 100644 --- a/examples/c++-arrow-dataflow/run.rs +++ b/examples/c++-arrow-dataflow/run.rs @@ -112,6 +112,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") @@ -136,6 +137,7 @@ async fn build_cxx_node( clang.arg("-l").arg("m"); clang.arg("-l").arg("rt"); clang.arg("-l").arg("dl"); + clang.arg("-l").arg("z"); clang.arg("-pthread"); } #[cfg(target_os = "windows")] diff --git a/examples/c++-dataflow/.gitignore b/examples/c++-dataflow/.gitignore index 5761abcf..d255f72c 100644 --- a/examples/c++-dataflow/.gitignore +++ b/examples/c++-dataflow/.gitignore @@ -1 +1,2 @@ *.o +/build diff --git a/examples/c++-dataflow/run.rs b/examples/c++-dataflow/run.rs index 6f966e19..dd88900a 100644 --- a/examples/c++-dataflow/run.rs +++ b/examples/c++-dataflow/run.rs @@ -133,6 +133,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") @@ -157,6 +158,7 @@ async fn build_cxx_node( clang.arg("-l").arg("m"); clang.arg("-l").arg("rt"); clang.arg("-l").arg("dl"); + clang.arg("-l").arg("z"); clang.arg("-pthread"); } #[cfg(target_os = "windows")] diff --git a/examples/c++-ros2-dataflow/.gitignore b/examples/c++-ros2-dataflow/.gitignore index 5761abcf..d255f72c 100644 --- a/examples/c++-ros2-dataflow/.gitignore +++ b/examples/c++-ros2-dataflow/.gitignore @@ -1 +1,2 @@ *.o +/build diff --git a/examples/c++-ros2-dataflow/run.rs b/examples/c++-ros2-dataflow/run.rs index 918158c2..4af3cd31 100644 --- a/examples/c++-ros2-dataflow/run.rs +++ b/examples/c++-ros2-dataflow/run.rs @@ -90,6 +90,7 @@ async fn build_cxx_node( clang.arg("-l").arg("m"); clang.arg("-l").arg("rt"); clang.arg("-l").arg("dl"); + clang.arg("-l").arg("z"); clang.arg("-pthread"); } #[cfg(target_os = "windows")] @@ -154,6 +155,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") diff --git a/examples/c-dataflow/run.rs b/examples/c-dataflow/run.rs index ad484edf..e71d802b 100644 --- a/examples/c-dataflow/run.rs +++ b/examples/c-dataflow/run.rs @@ -44,6 +44,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") @@ -63,6 +64,7 @@ async fn build_c_node(root: &Path, name: &str, out_name: &str) -> eyre::Result<( clang.arg("-l").arg("m"); clang.arg("-l").arg("rt"); clang.arg("-l").arg("dl"); + clang.arg("-l").arg("z"); clang.arg("-pthread"); } #[cfg(target_os = "windows")] @@ -93,6 +95,8 @@ async fn build_c_node(root: &Path, name: &str, out_name: &str) -> eyre::Result<( clang.arg("-lsynchronization"); clang.arg("-luser32"); clang.arg("-lwinspool"); + clang.arg("-lwinhttp"); + clang.arg("-lrpcrt4"); clang.arg("-Wl,-nodefaultlib:libcmt"); clang.arg("-D_DLL"); @@ -107,6 +111,7 @@ async fn build_c_node(root: &Path, name: &str, out_name: &str) -> eyre::Result<( clang.arg("-l").arg("pthread"); clang.arg("-l").arg("c"); clang.arg("-l").arg("m"); + clang.arg("-l").arg("z"); } clang.arg("-L").arg(root.join("target").join("debug")); clang @@ -161,6 +166,8 @@ async fn build_c_operator(root: &Path) -> eyre::Result<()> { link.arg("-lsynchronization"); link.arg("-luser32"); link.arg("-lwinspool"); + link.arg("-lwinhttp"); + link.arg("-lrpcrt4"); link.arg("-Wl,-nodefaultlib:libcmt"); link.arg("-D_DLL"); diff --git a/examples/camera/run.rs b/examples/camera/run.rs index 9c475c26..94988261 100644 --- a/examples/camera/run.rs +++ b/examples/camera/run.rs @@ -43,6 +43,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); @@ -51,6 +52,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("run").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); diff --git a/examples/cmake-dataflow/run.rs b/examples/cmake-dataflow/run.rs index 30e3c9d1..b4530f26 100644 --- a/examples/cmake-dataflow/run.rs +++ b/examples/cmake-dataflow/run.rs @@ -61,6 +61,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") 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/multiple-daemons/node/src/main.rs b/examples/multiple-daemons/node/src/main.rs index 36f42d57..bf1cd424 100644 --- a/examples/multiple-daemons/node/src/main.rs +++ b/examples/multiple-daemons/node/src/main.rs @@ -26,7 +26,7 @@ fn main() -> eyre::Result<()> { } other => eprintln!("Ignoring unexpected input `{other}`"), }, - Event::Stop => println!("Received manual stop"), + Event::Stop(_) => println!("Received stop"), other => eprintln!("Received unexpected input: {other:?}"), } } diff --git a/examples/multiple-daemons/run.rs b/examples/multiple-daemons/run.rs index 64410a8a..cb558af3 100644 --- a/examples/multiple-daemons/run.rs +++ b/examples/multiple-daemons/run.rs @@ -1,3 +1,4 @@ +use dora_cli::session::DataflowSession; use dora_coordinator::{ControlEvent, Event}; use dora_core::{ descriptor::{read_as_descriptor, DescriptorExt}, @@ -8,7 +9,7 @@ use dora_message::{ common::DaemonId, coordinator_to_cli::{ControlRequestReply, DataflowIdAndName}, }; -use dora_tracing::set_up_tracing; +use dora_tracing::TracingBuilder; use eyre::{bail, Context}; use std::{ @@ -29,7 +30,9 @@ use uuid::Uuid; #[tokio::main] async fn main() -> eyre::Result<()> { - set_up_tracing("multiple-daemon-runner").wrap_err("failed to set up tracing subscriber")?; + TracingBuilder::new("multiple-daemon-runner") + .with_stdout("debug") + .build()?; let root = Path::new(env!("CARGO_MANIFEST_DIR")); std::env::set_current_dir(root.join(file!()).parent().unwrap()) @@ -47,12 +50,15 @@ async fn main() -> eyre::Result<()> { IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), DORA_COORDINATOR_PORT_CONTROL_DEFAULT, ); - let (_coordinator_port, coordinator) = dora_coordinator::start( + let (coordinator_port, coordinator) = dora_coordinator::start( coordinator_bind, coordinator_control_bind, ReceiverStream::new(coordinator_events_rx), ) .await?; + + tracing::info!("coordinator running on {coordinator_port}"); + let coordinator_addr = Ipv4Addr::LOCALHOST; let daemon_a = run_daemon(coordinator_addr.to_string(), "A"); let daemon_b = run_daemon(coordinator_addr.to_string(), "B"); @@ -135,12 +141,17 @@ async fn start_dataflow( .check(&working_dir) .wrap_err("could not validate yaml")?; + let dataflow_session = + DataflowSession::read_session(dataflow).context("failed to read DataflowSession")?; + let (reply_sender, reply) = oneshot::channel(); coordinator_events_tx .send(Event::Control(ControlEvent::IncomingRequest { request: ControlRequest::Start { + build_id: dataflow_session.build_id, + session_id: dataflow_session.session_id, dataflow: dataflow_descriptor, - local_working_dir: working_dir, + local_working_dir: Some(working_dir), name: None, uv: false, }, @@ -149,7 +160,21 @@ async fn start_dataflow( .await?; let result = reply.await??; let uuid = match result { - ControlRequestReply::DataflowStarted { uuid } => uuid, + ControlRequestReply::DataflowStartTriggered { uuid } => uuid, + ControlRequestReply::Error(err) => bail!("{err}"), + other => bail!("unexpected start dataflow reply: {other:?}"), + }; + + let (reply_sender, reply) = oneshot::channel(); + coordinator_events_tx + .send(Event::Control(ControlEvent::IncomingRequest { + request: ControlRequest::WaitForSpawn { dataflow_id: uuid }, + reply_sender, + })) + .await?; + let result = reply.await??; + let uuid = match result { + ControlRequestReply::DataflowSpawned { uuid } => uuid, ControlRequestReply::Error(err) => bail!("{err}"), other => bail!("unexpected start dataflow reply: {other:?}"), }; @@ -215,6 +240,7 @@ async fn build_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow); if !cmd.status().await?.success() { bail!("failed to build dataflow"); @@ -227,6 +253,7 @@ async fn run_daemon(coordinator: String, machine_id: &str) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--machine-id") diff --git a/examples/multiple-daemons/sink/src/main.rs b/examples/multiple-daemons/sink/src/main.rs index e180af08..59316aba 100644 --- a/examples/multiple-daemons/sink/src/main.rs +++ b/examples/multiple-daemons/sink/src/main.rs @@ -24,8 +24,8 @@ fn main() -> eyre::Result<()> { } other => eprintln!("Ignoring unexpected input `{other}`"), }, - Event::Stop => { - println!("Received manual stop"); + Event::Stop(_) => { + println!("Received stop"); } Event::InputClosed { id } => { println!("Input `{id}` was closed"); diff --git a/examples/openai-server/dataflow-rust.yml b/examples/openai-server/dataflow-rust.yml index 8c6a1d8d..85668b5a 100644 --- a/examples/openai-server/dataflow-rust.yml +++ b/examples/openai-server/dataflow-rust.yml @@ -3,14 +3,14 @@ nodes: build: cargo build -p dora-openai-proxy-server --release path: ../../target/release/dora-openai-proxy-server outputs: - - chat_completion_request + - text inputs: - completion_reply: dora-echo/echo + text: dora-echo/echo - id: dora-echo build: pip install -e ../../node-hub/dora-echo path: dora-echo inputs: - echo: dora-openai-server/chat_completion_request + echo: dora-openai-server/text outputs: - echo diff --git a/examples/openai-server/openai_api_client.py b/examples/openai-server/openai_api_client.py index 0a88d5b1..4580d652 100644 --- a/examples/openai-server/openai_api_client.py +++ b/examples/openai-server/openai_api_client.py @@ -32,11 +32,69 @@ def test_chat_completion(user_input): print(f"Error in chat completion: {e}") +def test_chat_completion_image_url(user_input): + """TODO: Add docstring.""" + try: + response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "What is in this image?"}, + { + "type": "image_url", + "image_url": { + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" + }, + }, + ], + } + ], + ) + print("Chat Completion Response:") + print(response.choices[0].message.content) + except Exception as e: + print(f"Error in chat completion: {e}") + + +def test_chat_completion_image_base64(user_input): + """TODO: Add docstring.""" + try: + response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "What is in this image?"}, + { + "type": "image_url", + "image_url": { + "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=" + }, + }, + ], + } + ], + ) + print("Chat Completion Response:") + print(response.choices[0].message.content) + except Exception as e: + print(f"Error in chat completion: {e}") + + if __name__ == "__main__": print("Testing API endpoints...") - test_list_models() + # test_list_models() print("\n" + "=" * 50 + "\n") chat_input = input("Enter a message for chat completion: ") test_chat_completion(chat_input) + + print("\n" + "=" * 50 + "\n") + + test_chat_completion_image_url(chat_input) + print("\n" + "=" * 50 + "\n") + test_chat_completion_image_base64(chat_input) print("\n" + "=" * 50 + "\n") diff --git a/examples/openai-server/qwenvl.yml b/examples/openai-server/qwenvl.yml new file mode 100644 index 00000000..b737b3be --- /dev/null +++ b/examples/openai-server/qwenvl.yml @@ -0,0 +1,16 @@ +nodes: + - id: dora-openai-server + build: cargo build -p dora-openai-proxy-server --release + path: ../../target/release/dora-openai-proxy-server + outputs: + - text + inputs: + text: dora-qwen2.5-vl/text + + - id: dora-qwen2.5-vl + build: pip install -e ../../node-hub/dora-qwen2-5-vl + path: dora-qwen2-5-vl + inputs: + text: dora-openai-server/text + outputs: + - text diff --git a/examples/python-dataflow/run.rs b/examples/python-dataflow/run.rs index 23b254e2..de96795d 100644 --- a/examples/python-dataflow/run.rs +++ b/examples/python-dataflow/run.rs @@ -44,6 +44,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); @@ -52,6 +53,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("run").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); diff --git a/examples/python-multi-env/run.rs b/examples/python-multi-env/run.rs index 23b254e2..de96795d 100644 --- a/examples/python-multi-env/run.rs +++ b/examples/python-multi-env/run.rs @@ -44,6 +44,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); @@ -52,6 +53,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("run").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); diff --git a/examples/python-operator-dataflow/run.rs b/examples/python-operator-dataflow/run.rs index 9c475c26..94988261 100644 --- a/examples/python-operator-dataflow/run.rs +++ b/examples/python-operator-dataflow/run.rs @@ -43,6 +43,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); @@ -51,6 +52,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("run").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); diff --git a/examples/python-ros2-dataflow/run.rs b/examples/python-ros2-dataflow/run.rs index 23b254e2..de96795d 100644 --- a/examples/python-ros2-dataflow/run.rs +++ b/examples/python-ros2-dataflow/run.rs @@ -44,6 +44,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); @@ -52,6 +53,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("run").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); diff --git a/examples/rerun-viewer/run.rs b/examples/rerun-viewer/run.rs index 4785ba9b..243db076 100644 --- a/examples/rerun-viewer/run.rs +++ b/examples/rerun-viewer/run.rs @@ -43,6 +43,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); @@ -51,6 +52,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("run").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); diff --git a/examples/rust-dataflow-git/.gitignore b/examples/rust-dataflow-git/.gitignore new file mode 100644 index 00000000..1ac9aaff --- /dev/null +++ b/examples/rust-dataflow-git/.gitignore @@ -0,0 +1,4 @@ +/build +/git + +/dataflow.dora-session.yaml diff --git a/examples/rust-dataflow-git/README.md b/examples/rust-dataflow-git/README.md new file mode 100644 index 00000000..f4d2f3de --- /dev/null +++ b/examples/rust-dataflow-git/README.md @@ -0,0 +1,7 @@ +# Git-based Rust example + +To get started: + +```bash +cargo run --example rust-dataflow-git +``` diff --git a/examples/rust-dataflow-git/dataflow.yml b/examples/rust-dataflow-git/dataflow.yml new file mode 100644 index 00000000..cf06ede2 --- /dev/null +++ b/examples/rust-dataflow-git/dataflow.yml @@ -0,0 +1,29 @@ +nodes: + - id: rust-node + git: https://github.com/dora-rs/dora.git + rev: 64ab0d7c # pinned commit, update this when changing the message crate + build: cargo build -p rust-dataflow-example-node + path: target/debug/rust-dataflow-example-node + inputs: + tick: dora/timer/millis/10 + outputs: + - random + + - id: rust-status-node + git: https://github.com/dora-rs/dora.git + rev: 64ab0d7c # pinned commit, update this when changing the message crate + build: cargo build -p rust-dataflow-example-status-node + path: target/debug/rust-dataflow-example-status-node + inputs: + tick: dora/timer/millis/100 + random: rust-node/random + outputs: + - status + + - id: rust-sink + git: https://github.com/dora-rs/dora.git + rev: 64ab0d7c # pinned commit, update this when changing the message crate + build: cargo build -p rust-dataflow-example-sink + path: target/debug/rust-dataflow-example-sink + inputs: + message: rust-status-node/status diff --git a/examples/rust-dataflow-git/run.rs b/examples/rust-dataflow-git/run.rs new file mode 100644 index 00000000..855eb85b --- /dev/null +++ b/examples/rust-dataflow-git/run.rs @@ -0,0 +1,53 @@ +use dora_tracing::set_up_tracing; +use eyre::{bail, Context}; +use std::path::Path; + +#[tokio::main] +async fn main() -> eyre::Result<()> { + set_up_tracing("rust-dataflow-runner").wrap_err("failed to set up tracing subscriber")?; + + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + std::env::set_current_dir(root.join(file!()).parent().unwrap()) + .wrap_err("failed to set working dir")?; + + let args: Vec = std::env::args().collect(); + let dataflow = if args.len() > 1 { + Path::new(&args[1]) + } else { + Path::new("dataflow.yml") + }; + build_dataflow(dataflow).await?; + + run_dataflow(dataflow).await?; + + Ok(()) +} + +async fn build_dataflow(dataflow: &Path) -> eyre::Result<()> { + let cargo = std::env::var("CARGO").unwrap(); + let mut cmd = tokio::process::Command::new(&cargo); + cmd.arg("run"); + cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); + cmd.arg("--").arg("build").arg(dataflow); + if !cmd.status().await?.success() { + bail!("failed to build dataflow"); + }; + Ok(()) +} + +async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { + let cargo = std::env::var("CARGO").unwrap(); + let mut cmd = tokio::process::Command::new(&cargo); + cmd.arg("run"); + cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); + cmd.arg("--") + .arg("daemon") + .arg("--run-dataflow") + .arg(dataflow); + if !cmd.status().await?.success() { + bail!("failed to run dataflow"); + }; + Ok(()) +} diff --git a/examples/rust-dataflow-url/.gitignore b/examples/rust-dataflow-url/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/examples/rust-dataflow-url/.gitignore @@ -0,0 +1 @@ +/build diff --git a/examples/rust-dataflow-url/run.rs b/examples/rust-dataflow-url/run.rs index 6f511970..158e8ed9 100644 --- a/examples/rust-dataflow-url/run.rs +++ b/examples/rust-dataflow-url/run.rs @@ -23,6 +23,7 @@ async fn build_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow); if !cmd.status().await?.success() { bail!("failed to build dataflow"); @@ -35,6 +36,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") diff --git a/examples/rust-dataflow/node/src/main.rs b/examples/rust-dataflow/node/src/main.rs index 36f42d57..bf1cd424 100644 --- a/examples/rust-dataflow/node/src/main.rs +++ b/examples/rust-dataflow/node/src/main.rs @@ -26,7 +26,7 @@ fn main() -> eyre::Result<()> { } other => eprintln!("Ignoring unexpected input `{other}`"), }, - Event::Stop => println!("Received manual stop"), + Event::Stop(_) => println!("Received stop"), other => eprintln!("Received unexpected input: {other:?}"), } } diff --git a/examples/rust-dataflow/run.rs b/examples/rust-dataflow/run.rs index 213b65a0..855eb85b 100644 --- a/examples/rust-dataflow/run.rs +++ b/examples/rust-dataflow/run.rs @@ -16,7 +16,6 @@ async fn main() -> eyre::Result<()> { } else { Path::new("dataflow.yml") }; - build_dataflow(dataflow).await?; run_dataflow(dataflow).await?; @@ -29,6 +28,7 @@ async fn build_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow); if !cmd.status().await?.success() { bail!("failed to build dataflow"); @@ -41,6 +41,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") diff --git a/examples/rust-dataflow/sink-dynamic/src/main.rs b/examples/rust-dataflow/sink-dynamic/src/main.rs index 58f36e41..db5164b7 100644 --- a/examples/rust-dataflow/sink-dynamic/src/main.rs +++ b/examples/rust-dataflow/sink-dynamic/src/main.rs @@ -25,8 +25,8 @@ fn main() -> eyre::Result<()> { } other => eprintln!("Ignoring unexpected input `{other}`"), }, - Event::Stop => { - println!("Received manual stop"); + Event::Stop(_) => { + println!("Received stop"); } Event::InputClosed { id } => { println!("Input `{id}` was closed"); diff --git a/examples/rust-dataflow/sink/src/main.rs b/examples/rust-dataflow/sink/src/main.rs index e180af08..59316aba 100644 --- a/examples/rust-dataflow/sink/src/main.rs +++ b/examples/rust-dataflow/sink/src/main.rs @@ -24,8 +24,8 @@ fn main() -> eyre::Result<()> { } other => eprintln!("Ignoring unexpected input `{other}`"), }, - Event::Stop => { - println!("Received manual stop"); + Event::Stop(_) => { + println!("Received stop"); } Event::InputClosed { id } => { println!("Input `{id}` was closed"); diff --git a/examples/rust-dataflow/status-node/src/main.rs b/examples/rust-dataflow/status-node/src/main.rs index 09de8184..ff97b97e 100644 --- a/examples/rust-dataflow/status-node/src/main.rs +++ b/examples/rust-dataflow/status-node/src/main.rs @@ -29,7 +29,7 @@ fn main() -> eyre::Result<()> { } other => eprintln!("ignoring unexpected input {other}"), }, - Event::Stop => {} + Event::Stop(_) => {} Event::InputClosed { id } => { println!("input `{id}` was closed"); if *id == "random" { diff --git a/examples/rust-ros2-dataflow/node/src/main.rs b/examples/rust-ros2-dataflow/node/src/main.rs index 395a5e34..32ad5970 100644 --- a/examples/rust-ros2-dataflow/node/src/main.rs +++ b/examples/rust-ros2-dataflow/node/src/main.rs @@ -119,7 +119,7 @@ fn main() -> eyre::Result<()> { } other => eprintln!("Ignoring unexpected input `{other}`"), }, - Event::Stop => println!("Received manual stop"), + Event::Stop(_) => println!("Received stop"), other => eprintln!("Received unexpected input: {other:?}"), }, MergedEvent::External(pose) => { diff --git a/examples/rust-ros2-dataflow/run.rs b/examples/rust-ros2-dataflow/run.rs index a14dce48..f81a25d5 100644 --- a/examples/rust-ros2-dataflow/run.rs +++ b/examples/rust-ros2-dataflow/run.rs @@ -23,6 +23,7 @@ async fn build_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow); if !cmd.status().await?.success() { bail!("failed to build dataflow"); @@ -35,6 +36,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--") .arg("daemon") .arg("--run-dataflow") 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..9ddacd55 --- /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: so101/pose + 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 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 ``` 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..59afa7b5 --- /dev/null +++ b/examples/urdf/franka.yml @@ -0,0 +1,35 @@ +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 + 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: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 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..0a46f861 --- /dev/null +++ b/examples/urdf/kuka.yml @@ -0,0 +1,33 @@ +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 + 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: "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..3055fb93 --- /dev/null +++ b/examples/urdf/piper.yml @@ -0,0 +1,35 @@ +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 + 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: "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..395c4591 --- /dev/null +++ b/examples/urdf/so_arm101.yml @@ -0,0 +1,35 @@ +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 + 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: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.01 + ROTATION_TOLERANCE: 0.03 diff --git a/examples/urdf/ur5.yml b/examples/urdf/ur5.yml new file mode 100644 index 00000000..0f19291a --- /dev/null +++ b/examples/urdf/ur5.yml @@ -0,0 +1,35 @@ +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 + 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: "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/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/examples/urdf/z1.yml b/examples/urdf/z1.yml new file mode 100644 index 00000000..bea243d2 --- /dev/null +++ b/examples/urdf/z1.yml @@ -0,0 +1,35 @@ +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 + 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: "0. 0. 0. 1. 0. 0. 0." + POSITION_TOLERANCE: 0.001 + ROTATION_TOLERANCE: 0.001 diff --git a/examples/vggt/depth-to-avif.yaml b/examples/vggt/depth-to-avif.yaml new file mode 100644 index 00000000..a7f34fc4 --- /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: pip install -e ../../node-hub/dora-rav1e + inputs: + depth: dora-vggt/depth + outputs: + - depth + env: + ENCODING: avif + + - id: rav1e-image + path: dora-rav1e + build: pip install -e ../../node-hub/dora-rav1e + inputs: + image: dora-vggt/image + outputs: + - image + env: + ENCODING: avif + + - id: bench + path: image_saver.py + inputs: + vggt_image: 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/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 diff --git a/examples/vlm/run.rs b/examples/vlm/run.rs index 1ec38c80..742c3818 100644 --- a/examples/vlm/run.rs +++ b/examples/vlm/run.rs @@ -43,6 +43,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("build").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); @@ -51,6 +52,7 @@ async fn run_dataflow(dataflow: &Path) -> eyre::Result<()> { let mut cmd = tokio::process::Command::new(&cargo); cmd.arg("run"); cmd.arg("--package").arg("dora-cli"); + cmd.arg("--release"); cmd.arg("--").arg("run").arg(dataflow).arg("--uv"); if !cmd.status().await?.success() { bail!("failed to run dataflow"); diff --git a/libraries/arrow-convert/src/into_impls.rs b/libraries/arrow-convert/src/into_impls.rs index a8434694..b2174146 100644 --- a/libraries/arrow-convert/src/into_impls.rs +++ b/libraries/arrow-convert/src/into_impls.rs @@ -81,6 +81,20 @@ impl IntoArrow for NaiveTime { } } +impl IntoArrow for String { + type A = StringArray; + fn into_arrow(self) -> Self::A { + std::iter::once(Some(self)).collect() + } +} + +impl IntoArrow for Vec { + type A = StringArray; + fn into_arrow(self) -> Self::A { + StringArray::from(self) + } +} + impl IntoArrow for NaiveDateTime { type A = arrow::array::TimestampNanosecondArray; fn into_arrow(self) -> Self::A { diff --git a/libraries/core/Cargo.toml b/libraries/core/Cargo.toml index 8ad7952a..41f2e112 100644 --- a/libraries/core/Cargo.toml +++ b/libraries/core/Cargo.toml @@ -9,17 +9,25 @@ repository.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +build = ["dep:git2", "dep:url"] + [dependencies] dora-message = { workspace = true } eyre = "0.6.8" serde = { version = "1.0.136", features = ["derive"] } -serde_yaml = "0.9.11" +serde_yaml = { workspace = true } once_cell = "1.13.0" which = "5.0.0" uuid = { version = "1.7", features = ["serde", "v7"] } tracing = "0.1" serde-with-expand-env = "1.1.0" -tokio = { version = "1.24.1", features = ["fs", "process", "sync"] } +tokio = { version = "1.24.1", features = ["fs", "process", "sync", "rt"] } schemars = "0.8.19" serde_json = "1.0.117" log = { version = "0.4.21", features = ["serde"] } +dunce = "1.0.5" +itertools = "0.14" +url = { version = "2.5.4", optional = true } +git2 = { workspace = true, optional = true } +fs_extra = "1.3.0" diff --git a/libraries/core/src/build/build_command.rs b/libraries/core/src/build/build_command.rs new file mode 100644 index 00000000..ef31a6fd --- /dev/null +++ b/libraries/core/src/build/build_command.rs @@ -0,0 +1,83 @@ +use std::{ + collections::BTreeMap, + io::{BufRead, BufReader}, + path::Path, + process::{Command, Stdio}, +}; + +use dora_message::descriptor::EnvValue; +use eyre::{eyre, Context}; + +pub fn run_build_command( + build: &str, + working_dir: &Path, + uv: bool, + envs: &Option>, + stdout_tx: tokio::sync::mpsc::Sender>, +) -> eyre::Result<()> { + 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 { + for (key, value) in envs { + let value = value.to_string(); + cmd.env(key, value); + } + } + + cmd.current_dir(dunce::simplified(working_dir)); + + cmd.stdin(Stdio::null()); + cmd.stdout(Stdio::piped()); + cmd.stderr(Stdio::piped()); + + cmd.env("CLICOLOR", "1"); + cmd.env("CLICOLOR_FORCE", "1"); + + let mut child = cmd + .spawn() + .wrap_err_with(|| format!("failed to spawn `{}`", build))?; + + let child_stdout = BufReader::new(child.stdout.take().expect("failed to take stdout")); + let child_stderr = BufReader::new(child.stderr.take().expect("failed to take stderr")); + let stderr_tx = stdout_tx.clone(); + let stdout_tx = stdout_tx.clone(); + + std::thread::spawn(move || { + for line in child_stdout.lines() { + if stdout_tx.blocking_send(line).is_err() { + break; + } + } + }); + std::thread::spawn(move || { + for line in child_stderr.lines() { + if stderr_tx.blocking_send(line).is_err() { + break; + } + } + }); + + 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(()) +} diff --git a/libraries/core/src/build/git.rs b/libraries/core/src/build/git.rs new file mode 100644 index 00000000..7e06f2e0 --- /dev/null +++ b/libraries/core/src/build/git.rs @@ -0,0 +1,374 @@ +use crate::build::{BuildLogger, PrevGitSource}; +use dora_message::{common::LogLevel, DataflowId, SessionId}; +use eyre::{bail, ContextCompat, WrapErr}; +use git2::FetchOptions; +use itertools::Itertools; +use std::{ + collections::{BTreeMap, BTreeSet}, + path::{Path, PathBuf}, +}; +use url::Url; + +#[derive(Default)] +pub struct GitManager { + /// Directories that are currently in use by running dataflows. + pub clones_in_use: BTreeMap>, + /// Builds that are prepared, but not done yet. + prepared_builds: BTreeMap, + reuse_for: BTreeMap, +} + +#[derive(Default)] +struct PreparedBuild { + /// Clone dirs that will be created during the build process. + /// + /// This allows subsequent nodes to reuse the dirs. + planned_clone_dirs: BTreeSet, +} + +impl GitManager { + pub fn choose_clone_dir( + &mut self, + session_id: SessionId, + repo: String, + commit_hash: String, + prev_git: Option, + target_dir: &Path, + ) -> eyre::Result { + let repo_url = Url::parse(&repo).context("failed to parse git repository URL")?; + let clone_dir = Self::clone_dir_path(target_dir, &repo_url, &commit_hash)?; + + let prev_commit_hash = prev_git + .as_ref() + .filter(|p| p.git_source.repo == repo) + .map(|p| &p.git_source.commit_hash); + + if let Some(using) = self.clones_in_use.get(&clone_dir) { + if !using.is_empty() { + // The directory is currently in use by another dataflow. Rebuilding + // while a dataflow is running could lead to unintended behavior. + eyre::bail!( + "the build directory is still in use by the following \ + dataflows, please stop them before rebuilding: {}", + using.iter().join(", ") + ) + } + } + + let reuse = if self.clone_dir_ready(session_id, &clone_dir) { + // The directory already contains a checkout of the commit we're interested in. + // So we can simply reuse the directory without doing any additional git + // operations. + ReuseOptions::Reuse { + dir: clone_dir.clone(), + } + } else if let Some(previous_commit_hash) = prev_commit_hash { + // we might be able to update a previous clone + let prev_clone_dir = Self::clone_dir_path(target_dir, &repo_url, previous_commit_hash)?; + + if prev_clone_dir.exists() { + let still_needed = prev_git + .map(|g| g.still_needed_for_this_build) + .unwrap_or(false); + let used_by_others = self + .clones_in_use + .get(&prev_clone_dir) + .map(|ids| !ids.is_empty()) + .unwrap_or(false); + if still_needed || used_by_others { + // previous clone is still in use -> we cannot rename it, but we can copy it + ReuseOptions::CopyAndFetch { + from: prev_clone_dir, + target_dir: clone_dir.clone(), + commit_hash, + } + } else { + // there is an unused previous clone that is no longer needed -> rename it + ReuseOptions::RenameAndFetch { + from: prev_clone_dir, + target_dir: clone_dir.clone(), + commit_hash, + } + } + } else { + // no existing clone associated with previous build id + ReuseOptions::NewClone { + target_dir: clone_dir.clone(), + repo_url, + commit_hash, + } + } + } else { + // no previous build that we can reuse + ReuseOptions::NewClone { + target_dir: clone_dir.clone(), + repo_url, + commit_hash, + } + }; + self.register_ready_clone_dir(session_id, clone_dir); + + Ok(GitFolder { reuse }) + } + + pub fn in_use(&self, dir: &Path) -> bool { + self.clones_in_use + .get(dir) + .map(|ids| !ids.is_empty()) + .unwrap_or(false) + } + + pub fn clone_dir_ready(&self, session_id: SessionId, dir: &Path) -> bool { + self.prepared_builds + .get(&session_id) + .map(|p| p.planned_clone_dirs.contains(dir)) + .unwrap_or(false) + || dir.exists() + } + + pub fn register_ready_clone_dir(&mut self, session_id: SessionId, dir: PathBuf) -> bool { + self.prepared_builds + .entry(session_id) + .or_default() + .planned_clone_dirs + .insert(dir) + } + + fn clone_dir_path( + base_dir: &Path, + repo_url: &Url, + commit_hash: &String, + ) -> eyre::Result { + let mut path = base_dir.join(repo_url.host_str().context("git URL has no hostname")?); + path.extend(repo_url.path_segments().context("no path in git URL")?); + let path = path.join(commit_hash); + Ok(dunce::simplified(&path).to_owned()) + } + + pub fn clear_planned_builds(&mut self, session_id: SessionId) { + self.prepared_builds.remove(&session_id); + } +} + +pub struct GitFolder { + /// Specifies whether an existing repo should be reused. + reuse: ReuseOptions, +} + +impl GitFolder { + pub async fn prepare(self, logger: &mut impl BuildLogger) -> eyre::Result { + let GitFolder { reuse } = self; + + tracing::info!("reuse: {reuse:?}"); + let clone_dir = match reuse { + ReuseOptions::NewClone { + target_dir, + repo_url, + commit_hash, + } => { + logger + .log_message( + LogLevel::Info, + format!( + "cloning {repo_url}#{commit_hash} into {}", + target_dir.display() + ), + ) + .await; + let clone_target = target_dir.clone(); + let checkout_result = tokio::task::spawn_blocking(move || { + let repository = clone_into(repo_url.clone(), &clone_target) + .with_context(|| format!("failed to clone git repo from `{repo_url}`"))?; + checkout_tree(&repository, &commit_hash) + .with_context(|| format!("failed to checkout commit `{commit_hash}`")) + }) + .await + .unwrap(); + + match checkout_result { + Ok(()) => target_dir, + Err(err) => { + logger + .log_message(LogLevel::Error, format!("{err:?}")) + .await; + // remove erroneous clone again + if let Err(err) = std::fs::remove_dir_all(target_dir) { + logger + .log_message( + LogLevel::Error, + format!( + "failed to remove clone dir after clone/checkout error: {}", + err.kind() + ), + ) + .await; + } + bail!(err) + } + } + } + ReuseOptions::CopyAndFetch { + from, + target_dir, + commit_hash, + } => { + let from_clone = from.clone(); + let to = target_dir.clone(); + tokio::task::spawn_blocking(move || { + std::fs::create_dir_all(&to) + .context("failed to create directory for copying git repo")?; + fs_extra::dir::copy( + &from_clone, + &to, + &fs_extra::dir::CopyOptions::new().content_only(true), + ) + .with_context(|| { + format!( + "failed to copy repo clone from `{}` to `{}`", + from_clone.display(), + to.display() + ) + }) + }) + .await??; + + logger + .log_message( + LogLevel::Info, + format!("fetching changes after copying {}", from.display()), + ) + .await; + + let repository = fetch_changes(&target_dir, None).await?; + checkout_tree(&repository, &commit_hash)?; + target_dir + } + ReuseOptions::RenameAndFetch { + from, + target_dir, + commit_hash, + } => { + tokio::fs::rename(&from, &target_dir) + .await + .context("failed to rename repo clone")?; + + logger + .log_message( + LogLevel::Info, + format!("fetching changes after renaming {}", from.display()), + ) + .await; + + let repository = fetch_changes(&target_dir, None).await?; + checkout_tree(&repository, &commit_hash)?; + target_dir + } + ReuseOptions::Reuse { dir } => { + logger + .log_message( + LogLevel::Info, + format!("reusing up-to-date {}", dir.display()), + ) + .await; + dir + } + }; + Ok(clone_dir) + } +} + +#[derive(Debug)] +enum ReuseOptions { + /// Create a new clone of the repository. + NewClone { + target_dir: PathBuf, + repo_url: Url, + commit_hash: String, + }, + /// Reuse an existing up-to-date clone of the repository. + Reuse { dir: PathBuf }, + /// Copy an older clone of the repository and fetch changes, then reuse it. + CopyAndFetch { + from: PathBuf, + target_dir: PathBuf, + commit_hash: String, + }, + /// Rename an older clone of the repository and fetch changes, then reuse it. + RenameAndFetch { + from: PathBuf, + target_dir: PathBuf, + commit_hash: String, + }, +} + +fn clone_into(repo_addr: Url, clone_dir: &Path) -> eyre::Result { + if let Some(parent) = clone_dir.parent() { + std::fs::create_dir_all(parent) + .context("failed to create parent directory for git clone")?; + } + + let clone_dir = clone_dir.to_owned(); + + let mut builder = git2::build::RepoBuilder::new(); + let mut fetch_options = git2::FetchOptions::new(); + fetch_options.download_tags(git2::AutotagOption::All); + builder.fetch_options(fetch_options); + builder + .clone(repo_addr.as_str(), &clone_dir) + .context("failed to clone repo") +} + +async fn fetch_changes( + repo_dir: &Path, + refname: Option, +) -> Result { + let repo_dir = repo_dir.to_owned(); + let fetch_changes = tokio::task::spawn_blocking(move || { + let repository = git2::Repository::open(&repo_dir).context("failed to open git repo")?; + + { + let mut remote = repository + .find_remote("origin") + .context("failed to find remote `origin` in repo")?; + remote + .connect(git2::Direction::Fetch) + .context("failed to connect to remote")?; + let default_branch = remote + .default_branch() + .context("failed to get default branch for remote")?; + let fetch = match &refname { + Some(refname) => refname, + None => default_branch + .as_str() + .context("failed to read default branch as string")?, + }; + let mut fetch_options = FetchOptions::new(); + fetch_options.download_tags(git2::AutotagOption::All); + remote + .fetch(&[&fetch], Some(&mut fetch_options), None) + .context("failed to fetch from git repo")?; + } + Result::<_, eyre::Error>::Ok(repository) + }); + let repository = fetch_changes.await??; + Ok(repository) +} + +fn checkout_tree(repository: &git2::Repository, commit_hash: &str) -> eyre::Result<()> { + let (object, reference) = repository + .revparse_ext(commit_hash) + .context("failed to parse ref")?; + repository + .checkout_tree(&object, None) + .context("failed to checkout ref")?; + match reference { + Some(reference) => repository + .set_head(reference.name().context("failed to get reference_name")?) + .context("failed to set head")?, + None => repository + .set_head_detached(object.id()) + .context("failed to set detached head")?, + } + + Ok(()) +} diff --git a/libraries/core/src/build/logger.rs b/libraries/core/src/build/logger.rs new file mode 100644 index 00000000..c382b1ac --- /dev/null +++ b/libraries/core/src/build/logger.rs @@ -0,0 +1,19 @@ +use std::future::Future; + +pub use dora_message::common::LogLevelOrStdout; + +pub trait BuildLogger: Send { + type Clone: BuildLogger + 'static; + + fn log_message( + &mut self, + level: impl Into + Send, + message: impl Into + Send, + ) -> impl Future + Send; + + fn log_stdout(&mut self, message: impl Into + Send) -> impl Future + Send { + self.log_message(LogLevelOrStdout::Stdout, message) + } + + fn try_clone(&self) -> impl Future> + Send; +} diff --git a/libraries/core/src/build/mod.rs b/libraries/core/src/build/mod.rs new file mode 100644 index 00000000..5e7193d5 --- /dev/null +++ b/libraries/core/src/build/mod.rs @@ -0,0 +1,148 @@ +pub use git::GitManager; +pub use logger::{BuildLogger, LogLevelOrStdout}; + +use url::Url; + +use std::{collections::BTreeMap, future::Future, path::PathBuf}; + +use crate::descriptor::ResolvedNode; +use dora_message::{ + common::{GitSource, LogLevel}, + descriptor::{CoreNodeKind, EnvValue}, + id::NodeId, + SessionId, +}; +use eyre::Context; + +use build_command::run_build_command; +use git::GitFolder; + +mod build_command; +mod git; +mod logger; + +#[derive(Clone)] +pub struct Builder { + pub session_id: SessionId, + pub base_working_dir: PathBuf, + pub uv: bool, +} + +impl Builder { + pub async fn build_node( + self, + node: ResolvedNode, + git: Option, + prev_git: Option, + mut logger: impl BuildLogger, + git_manager: &mut GitManager, + ) -> eyre::Result>> { + let prepared_git = if let Some(GitSource { repo, commit_hash }) = git { + let target_dir = self.base_working_dir.join("git"); + let git_folder = git_manager.choose_clone_dir( + self.session_id, + repo, + commit_hash, + prev_git, + &target_dir, + )?; + Some(git_folder) + } else { + None + }; + + let task = async move { self.build_node_inner(node, &mut logger, prepared_git).await }; + Ok(task) + } + + async fn build_node_inner( + self, + node: ResolvedNode, + logger: &mut impl BuildLogger, + git_folder: Option, + ) -> eyre::Result { + logger.log_message(LogLevel::Debug, "building node").await; + let node_working_dir = match &node.kind { + CoreNodeKind::Custom(n) => { + let node_working_dir = match git_folder { + Some(git_folder) => { + let clone_dir = git_folder.prepare(logger).await?; + tracing::warn!( + "using git clone directory as working dir: \ + this behavior is unstable and might change \ + (see https://github.com/dora-rs/dora/pull/901)" + ); + clone_dir + } + None => self.base_working_dir, + }; + + if let Some(build) = &n.build { + build_node(logger, &node.env, node_working_dir.clone(), build, self.uv).await?; + } + node_working_dir + } + CoreNodeKind::Runtime(n) => { + // run build commands + for operator in &n.operators { + if let Some(build) = &operator.config.build { + build_node( + logger, + &node.env, + self.base_working_dir.clone(), + build, + self.uv, + ) + .await?; + } + } + self.base_working_dir.clone() + } + }; + Ok(BuiltNode { node_working_dir }) + } +} + +async fn build_node( + logger: &mut impl BuildLogger, + node_env: &Option>, + working_dir: PathBuf, + build: &String, + uv: bool, +) -> eyre::Result<()> { + logger + .log_message(LogLevel::Info, format!("running build command: `{build}")) + .await; + let build = build.to_owned(); + let node_env = node_env.clone(); + let mut logger = logger.try_clone().await.context("failed to clone logger")?; + let (stdout_tx, mut stdout) = tokio::sync::mpsc::channel(10); + let task = tokio::task::spawn_blocking(move || { + run_build_command(&build, &working_dir, uv, &node_env, stdout_tx) + .context("build command failed") + }); + tokio::spawn(async move { + while let Some(line) = stdout.recv().await { + logger + .log_stdout(line.unwrap_or_else(|err| format!("io err: {}", err.kind()))) + .await; + } + }); + task.await??; + Ok(()) +} + +pub struct BuiltNode { + pub node_working_dir: PathBuf, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct BuildInfo { + pub node_working_dirs: BTreeMap, +} + +pub struct PrevGitSource { + pub git_source: GitSource, + /// `True` if any nodes of this dataflow still require the source for building. + pub still_needed_for_this_build: bool, +} diff --git a/libraries/core/src/descriptor/mod.rs b/libraries/core/src/descriptor/mod.rs index cb8860fa..c3cd910a 100644 --- a/libraries/core/src/descriptor/mod.rs +++ b/libraries/core/src/descriptor/mod.rs @@ -1,5 +1,6 @@ use dora_message::{ config::{Input, InputMapping, NodeRunConfig}, + descriptor::{GitRepoRev, NodeSource}, id::{DataId, NodeId, OperatorId}, }; use eyre::{bail, Context, OptionExt, Result}; @@ -53,7 +54,7 @@ impl DescriptorExt for Descriptor { // adjust input mappings let mut node_kind = node_kind_mut(&mut node)?; let input_mappings: Vec<_> = match &mut node_kind { - NodeKindMut::Standard { path: _, inputs } => inputs.values_mut().collect(), + NodeKindMut::Standard { inputs, .. } => inputs.values_mut().collect(), NodeKindMut::Runtime(node) => node .operators .iter_mut() @@ -76,8 +77,13 @@ impl DescriptorExt for Descriptor { // resolve nodes let kind = match node_kind { - NodeKindMut::Standard { path, inputs: _ } => CoreNodeKind::Custom(CustomNode { - source: path.clone(), + NodeKindMut::Standard { + path, + source, + inputs: _, + } => CoreNodeKind::Custom(CustomNode { + path: path.clone(), + source, args: node.args, build: node.build, send_stdout_as: node.send_stdout_as, @@ -149,14 +155,35 @@ pub async fn read_as_descriptor(path: &Path) -> eyre::Result { fn node_kind_mut(node: &mut Node) -> eyre::Result { match node.kind()? { - NodeKind::Standard(_) => node - .path - .as_ref() - .map(|path| NodeKindMut::Standard { - path, + NodeKind::Standard(_) => { + let source = match (&node.git, &node.branch, &node.tag, &node.rev) { + (None, None, None, None) => NodeSource::Local, + (Some(repo), branch, tag, rev) => { + let rev = match (branch, tag, rev) { + (None, None, None) => None, + (Some(branch), None, None) => Some(GitRepoRev::Branch(branch.clone())), + (None, Some(tag), None) => Some(GitRepoRev::Tag(tag.clone())), + (None, None, Some(rev)) => Some(GitRepoRev::Rev(rev.clone())), + other @ (_, _, _) => { + eyre::bail!("only one of `branch`, `tag`, and `rev` are allowed (got {other:?})") + } + }; + NodeSource::GitBranch { + repo: repo.clone(), + rev, + } + } + (None, _, _, _) => { + eyre::bail!("`git` source required when using branch, tag, or rev") + } + }; + + Ok(NodeKindMut::Standard { + path: node.path.as_ref().ok_or_eyre("missing `path` attribute")?, + source, inputs: &mut node.inputs, }) - .ok_or_eyre("no path"), + } NodeKind::Runtime(_) => node .operators .as_mut() @@ -249,6 +276,7 @@ pub enum NodeKind<'a> { enum NodeKindMut<'a> { Standard { path: &'a String, + source: NodeSource, inputs: &'a mut BTreeMap, }, /// Dora runtime node diff --git a/libraries/core/src/descriptor/validate.rs b/libraries/core/src/descriptor/validate.rs index c28bd451..da4a3798 100644 --- a/libraries/core/src/descriptor/validate.rs +++ b/libraries/core/src/descriptor/validate.rs @@ -28,23 +28,34 @@ pub fn check_dataflow( // check that nodes and operators exist for node in nodes.values() { match &node.kind { - descriptor::CoreNodeKind::Custom(custom) => match custom.source.as_str() { - SHELL_SOURCE => (), - DYNAMIC_SOURCE => (), - source => { - if source_is_url(source) { - info!("{source} is a URL."); // TODO: Implement url check. - } else if let Some(remote_daemon_id) = remote_daemon_id { - if let Some(machine) = &node.deploy.machine { - if remote_daemon_id.contains(&machine.as_str()) || coordinator_is_remote - { - info!("skipping path check for remote node `{}`", node.id); + descriptor::CoreNodeKind::Custom(custom) => match &custom.source { + dora_message::descriptor::NodeSource::Local => match custom.path.as_str() { + SHELL_SOURCE => (), + DYNAMIC_SOURCE => (), + source => { + if source_is_url(source) { + info!("{source} is a URL."); // TODO: Implement url check. + } else if let Some(remote_daemon_id) = remote_daemon_id { + if let Some(deploy) = &node.deploy { + if let Some(machine) = &deploy.machine { + if remote_daemon_id.contains(&machine.as_str()) + || coordinator_is_remote + { + info!("skipping path check for remote node `{}`", node.id); + } + } } - } - } else { - resolve_path(source, working_dir) - .wrap_err_with(|| format!("Could not find source path `{}`", source))?; - }; + } else if custom.build.is_some() { + info!("skipping path check for node with build command"); + } else { + resolve_path(source, working_dir).wrap_err_with(|| { + format!("Could not find source path `{}`", source) + })?; + }; + } + }, + dora_message::descriptor::NodeSource::GitBranch { .. } => { + info!("skipping check for node with git source"); } }, descriptor::CoreNodeKind::Runtime(node) => { @@ -53,6 +64,8 @@ pub fn check_dataflow( OperatorSource::SharedLibrary(path) => { if source_is_url(path) { info!("{path} is a URL."); // TODO: Implement url check. + } else if operator_definition.config.build.is_some() { + info!("skipping path check for operator with build command"); } else { let path = adjust_shared_library_path(Path::new(&path))?; if !working_dir.join(&path).exists() { diff --git a/libraries/core/src/lib.rs b/libraries/core/src/lib.rs index c7e7cd6c..c45ec613 100644 --- a/libraries/core/src/lib.rs +++ b/libraries/core/src/lib.rs @@ -7,6 +7,8 @@ use std::{ pub use dora_message::{config, uhlc}; +#[cfg(feature = "build")] +pub mod build; pub mod descriptor; pub mod metadata; pub mod topics; 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/libraries/message/Cargo.toml b/libraries/message/Cargo.toml index 7bb3f673..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.4.4" +version = "0.5.0" edition.workspace = true documentation.workspace = true description.workspace = true @@ -23,7 +23,7 @@ aligned-vec = { version = "0.5.0", features = ["serde"] } semver = { version = "1.0.23", features = ["serde"] } schemars = "0.8.19" uhlc = "0.5.1" -serde_yaml = "0.9.11" +serde_yaml = { workspace = true } once_cell = "1.13.0" serde-with-expand-env = "1.1.0" bincode = "1.3.3" diff --git a/libraries/message/src/cli_to_coordinator.rs b/libraries/message/src/cli_to_coordinator.rs index 1b62fd58..bf3d3a03 100644 --- a/libraries/message/src/cli_to_coordinator.rs +++ b/libraries/message/src/cli_to_coordinator.rs @@ -1,22 +1,52 @@ -use std::{path::PathBuf, time::Duration}; +use std::{collections::BTreeMap, path::PathBuf, time::Duration}; use uuid::Uuid; use crate::{ + common::GitSource, descriptor::Descriptor, id::{NodeId, OperatorId}, + BuildId, SessionId, }; -#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub enum ControlRequest { + Build { + session_id: SessionId, + dataflow: Descriptor, + git_sources: BTreeMap, + prev_git_sources: BTreeMap, + /// Allows overwriting the base working dir when CLI and daemon are + /// running on the same machine. + /// + /// Must not be used for multi-machine dataflows. + /// + /// Note that nodes with git sources still use a subdirectory of + /// the base working dir. + local_working_dir: Option, + uv: bool, + }, + WaitForBuild { + build_id: BuildId, + }, Start { + build_id: Option, + session_id: SessionId, dataflow: Descriptor, name: Option, - // TODO: remove this once we figure out deploying of node/operator - // binaries from CLI to coordinator/daemon - local_working_dir: PathBuf, + /// Allows overwriting the base working dir when CLI and daemon are + /// running on the same machine. + /// + /// Must not be used for multi-machine dataflows. + /// + /// Note that nodes with git sources still use a subdirectory of + /// the base working dir. + local_working_dir: Option, uv: bool, }, + WaitForSpawn { + dataflow_id: Uuid, + }, Reload { dataflow_id: Uuid, node_id: NodeId, @@ -46,4 +76,9 @@ pub enum ControlRequest { dataflow_id: Uuid, level: log::LevelFilter, }, + BuildLogSubscribe { + build_id: BuildId, + level: log::LevelFilter, + }, + CliAndDefaultDaemonOnSameMachine, } diff --git a/libraries/message/src/common.rs b/libraries/message/src/common.rs index 93e2f8d9..d48f1308 100644 --- a/libraries/message/src/common.rs +++ b/libraries/message/src/common.rs @@ -5,17 +5,18 @@ use aligned_vec::{AVec, ConstAlign}; use eyre::Context as _; use uuid::Uuid; -use crate::{daemon_to_daemon::InterDaemonEvent, id::NodeId, DataflowId}; +use crate::{daemon_to_daemon::InterDaemonEvent, id::NodeId, BuildId, DataflowId}; pub use log::Level as LogLevel; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[must_use] pub struct LogMessage { - pub dataflow_id: DataflowId, + pub build_id: Option, + pub dataflow_id: Option, pub node_id: Option, pub daemon_id: Option, - pub level: LogLevel, + pub level: LogLevelOrStdout, pub target: Option, pub module_path: Option, pub file: Option, @@ -23,6 +24,18 @@ pub struct LogMessage { pub message: String, } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub enum LogLevelOrStdout { + LogLevel(LogLevel), + Stdout, +} + +impl From for LogLevelOrStdout { + fn from(level: LogLevel) -> Self { + Self::LogLevel(level) + } +} + #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub struct NodeError { pub timestamp: uhlc::Timestamp, @@ -32,6 +45,9 @@ pub struct NodeError { impl std::fmt::Display for NodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let NodeErrorCause::FailedToSpawn(err) = &self.cause { + return write!(f, "failed to spawn node: {err}"); + } match &self.exit_status { NodeExitStatus::Success => write!(f, ""), NodeExitStatus::IoError(err) => write!(f, "I/O error while reading exit status: {err}"), @@ -68,6 +84,7 @@ impl std::fmt::Display for NodeError { f, ". This error occurred because node `{caused_by_node}` exited before connecting to dora." )?, + NodeErrorCause::FailedToSpawn(_) => unreachable!(), // handled above NodeErrorCause::Other { stderr } if stderr.is_empty() => {} NodeErrorCause::Other { stderr } => { let line: &str = "---------------------------------------------------------------------------------\n"; @@ -88,6 +105,7 @@ pub enum NodeErrorCause { Cascading { caused_by_node: NodeId, }, + FailedToSpawn(String), Other { stderr: String, }, @@ -234,3 +252,9 @@ impl std::fmt::Display for DaemonId { write!(f, "{}", self.uuid) } } + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)] +pub struct GitSource { + pub repo: String, + pub commit_hash: String, +} diff --git a/libraries/message/src/coordinator_to_cli.rs b/libraries/message/src/coordinator_to_cli.rs index c8f1d3c8..02243468 100644 --- a/libraries/message/src/coordinator_to_cli.rs +++ b/libraries/message/src/coordinator_to_cli.rs @@ -1,22 +1,46 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::{ + collections::{BTreeMap, BTreeSet}, + net::IpAddr, +}; use uuid::Uuid; pub use crate::common::{LogLevel, LogMessage, NodeError, NodeErrorCause, NodeExitStatus}; -use crate::{common::DaemonId, id::NodeId}; +use crate::{common::DaemonId, id::NodeId, BuildId}; #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub enum ControlRequestReply { Error(String), CoordinatorStopped, - DataflowStarted { uuid: Uuid }, - DataflowReloaded { uuid: Uuid }, - DataflowStopped { uuid: Uuid, result: DataflowResult }, + DataflowBuildTriggered { + build_id: BuildId, + }, + DataflowBuildFinished { + build_id: BuildId, + result: Result<(), String>, + }, + DataflowStartTriggered { + uuid: Uuid, + }, + DataflowSpawned { + uuid: Uuid, + }, + DataflowReloaded { + uuid: Uuid, + }, + DataflowStopped { + uuid: Uuid, + result: DataflowResult, + }, DataflowList(DataflowList), DestroyOk, DaemonConnected(bool), ConnectedDaemons(BTreeSet), Logs(Vec), + CliAndDefaultDaemonIps { + default_daemon: Option, + cli: Option, + }, } #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] diff --git a/libraries/message/src/coordinator_to_daemon.rs b/libraries/message/src/coordinator_to_daemon.rs index 482f0042..69da8923 100644 --- a/libraries/message/src/coordinator_to_daemon.rs +++ b/libraries/message/src/coordinator_to_daemon.rs @@ -5,10 +5,10 @@ use std::{ }; use crate::{ - common::DaemonId, + common::{DaemonId, GitSource}, descriptor::{Descriptor, ResolvedNode}, id::{NodeId, OperatorId}, - DataflowId, + BuildId, DataflowId, SessionId, }; pub use crate::common::Timestamped; @@ -33,6 +33,7 @@ impl RegisterResult { #[derive(Debug, serde::Deserialize, serde::Serialize)] pub enum DaemonCoordinatorEvent { + Build(BuildDataflowNodes), Spawn(SpawnDataflowNodes), AllNodesReady { dataflow_id: DataflowId, @@ -55,10 +56,38 @@ pub enum DaemonCoordinatorEvent { Heartbeat, } +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct BuildDataflowNodes { + pub build_id: BuildId, + pub session_id: SessionId, + /// Allows overwriting the base working dir when CLI and daemon are + /// running on the same machine. + /// + /// Must not be used for multi-machine dataflows. + /// + /// Note that nodes with git sources still use a subdirectory of + /// the base working dir. + pub local_working_dir: Option, + pub git_sources: BTreeMap, + pub prev_git_sources: BTreeMap, + pub dataflow_descriptor: Descriptor, + pub nodes_on_machine: BTreeSet, + pub uv: bool, +} + #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct SpawnDataflowNodes { + pub build_id: Option, + pub session_id: SessionId, pub dataflow_id: DataflowId, - pub working_dir: PathBuf, + /// Allows overwriting the base working dir when CLI and daemon are + /// running on the same machine. + /// + /// Must not be used for multi-machine dataflows. + /// + /// Note that nodes with git sources still use a subdirectory of + /// the base working dir. + pub local_working_dir: Option, pub nodes: BTreeMap, pub dataflow_descriptor: Descriptor, pub spawn_nodes: BTreeSet, diff --git a/libraries/message/src/daemon_to_coordinator.rs b/libraries/message/src/daemon_to_coordinator.rs index 22bd0e5f..ccafb0a5 100644 --- a/libraries/message/src/daemon_to_coordinator.rs +++ b/libraries/message/src/daemon_to_coordinator.rs @@ -3,7 +3,9 @@ use std::collections::BTreeMap; pub use crate::common::{ DataMessage, LogLevel, LogMessage, NodeError, NodeErrorCause, NodeExitStatus, Timestamped, }; -use crate::{common::DaemonId, current_crate_version, id::NodeId, versions_compatible, DataflowId}; +use crate::{ + common::DaemonId, current_crate_version, id::NodeId, versions_compatible, BuildId, DataflowId, +}; #[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum CoordinatorRequest { @@ -46,6 +48,14 @@ impl DaemonRegisterRequest { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum DaemonEvent { + BuildResult { + build_id: BuildId, + result: Result<(), String>, + }, + SpawnResult { + dataflow_id: DataflowId, + result: Result<(), String>, + }, AllNodesReady { dataflow_id: DataflowId, exited_before_subscribe: Vec, @@ -73,7 +83,8 @@ impl DataflowDaemonResult { #[derive(Debug, serde::Deserialize, serde::Serialize)] pub enum DaemonCoordinatorReply { - SpawnResult(Result<(), String>), + TriggerBuildResult(Result<(), String>), + TriggerSpawnResult(Result<(), String>), ReloadResult(Result<(), String>), StopResult(Result<(), String>), DestroyResult { diff --git a/libraries/message/src/daemon_to_node.rs b/libraries/message/src/daemon_to_node.rs index acc1630e..75c59bba 100644 --- a/libraries/message/src/daemon_to_node.rs +++ b/libraries/message/src/daemon_to_node.rs @@ -2,7 +2,7 @@ use std::{net::SocketAddr, path::PathBuf}; use crate::{ config::NodeRunConfig, - descriptor::{Descriptor, OperatorDefinition}, + descriptor::OperatorDefinition, id::{DataId, NodeId, OperatorId}, metadata::Metadata, DataflowId, @@ -23,7 +23,7 @@ pub struct NodeConfig { pub node_id: NodeId, pub run_config: NodeRunConfig, pub daemon_communication: DaemonCommunication, - pub dataflow_descriptor: Descriptor, + pub dataflow_descriptor: serde_yaml::Value, pub dynamic: bool, } diff --git a/libraries/message/src/descriptor.rs b/libraries/message/src/descriptor.rs index 2fe68760..f6a2ba9c 100644 --- a/libraries/message/src/descriptor.rs +++ b/libraries/message/src/descriptor.rs @@ -23,18 +23,19 @@ pub struct Descriptor { #[serde(default)] pub communication: CommunicationConfig, #[schemars(skip)] - #[serde(default, rename = "_unstable_deploy")] - pub deploy: Deploy, + #[serde(rename = "_unstable_deploy")] + pub deploy: Option, pub nodes: Vec, #[schemars(skip)] #[serde(default, rename = "_unstable_debug")] pub debug: Debug, } -#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct Deploy { pub machine: Option, + pub working_dir: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] @@ -58,8 +59,8 @@ pub struct Node { /// Unstable machine deployment configuration #[schemars(skip)] - #[serde(default, rename = "_unstable_deploy")] - pub deploy: Deploy, + #[serde(rename = "_unstable_deploy")] + pub deploy: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub operators: Option, @@ -70,6 +71,15 @@ pub struct Node { #[serde(default, skip_serializing_if = "Option::is_none")] pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub git: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub branch: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub rev: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub args: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -90,7 +100,7 @@ pub struct ResolvedNode { pub env: Option>, #[serde(default)] - pub deploy: Deploy, + pub deploy: Option, #[serde(flatten)] pub kind: CoreNodeKind, @@ -216,7 +226,8 @@ pub struct CustomNode { /// args: some_node.py /// /// Source can match any executable in PATH. - pub source: String, + pub path: String, + pub source: NodeSource, /// Args for the executable. #[serde(default, skip_serializing_if = "Option::is_none")] pub args: Option, @@ -234,6 +245,28 @@ pub struct CustomNode { pub run_config: NodeRunConfig, } +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub enum NodeSource { + Local, + GitBranch { + repo: String, + rev: Option, + }, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub enum ResolvedNodeSource { + Local, + GitCommit { repo: String, commit_hash: String }, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub enum GitRepoRev { + Branch(String), + Tag(String), + Rev(String), +} + #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(untagged)] pub enum EnvValue { diff --git a/libraries/message/src/lib.rs b/libraries/message/src/lib.rs index 9d1870e0..e5e2e33f 100644 --- a/libraries/message/src/lib.rs +++ b/libraries/message/src/lib.rs @@ -24,9 +24,42 @@ pub mod coordinator_to_cli; pub use arrow_data; pub use arrow_schema; +use uuid::{Timestamp, Uuid}; pub type DataflowId = uuid::Uuid; +#[derive( + Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, +)] +pub struct SessionId(uuid::Uuid); + +impl SessionId { + pub fn generate() -> Self { + Self(Uuid::new_v7(Timestamp::now(uuid::NoContext))) + } + + pub fn uuid(&self) -> uuid::Uuid { + self.0 + } +} + +#[derive( + Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, +)] +pub struct BuildId(uuid::Uuid); + +impl BuildId { + pub fn generate() -> Self { + Self(Uuid::new_v7(Timestamp::now(uuid::NoContext))) + } +} + +impl std::fmt::Display for BuildId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "BuildId({})", self.0) + } +} + fn current_crate_version() -> semver::Version { let crate_version_raw = env!("CARGO_PKG_VERSION"); 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/dora_keyboard/main.py b/node-hub/dora-keyboard/dora_keyboard/main.py index f3858fe0..644c10c5 100644 --- a/node-hub/dora-keyboard/dora_keyboard/main.py +++ b/node-hub/dora-keyboard/dora_keyboard/main.py @@ -11,6 +11,8 @@ def main(): node = Node() always_none = node.next(timeout=0.001) is None + always_none = node.next(timeout=0.001) is None + print("Always None:", always_none) with keyboard.Events() as events: while True: if not always_none: 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/dora_microphone/main.py b/node-hub/dora-microphone/dora_microphone/main.py index b45756bc..aec0eeab 100644 --- a/node-hub/dora-microphone/dora_microphone/main.py +++ b/node-hub/dora-microphone/dora_microphone/main.py @@ -19,6 +19,7 @@ def main(): start_recording_time = tm.time() node = Node() + always_none = node.next(timeout=0.001) is None always_none = node.next(timeout=0.001) is None finished = False 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-mistral-rs/src/main.rs b/node-hub/dora-mistral-rs/src/main.rs index bb451e1e..e9227633 100644 --- a/node-hub/dora-mistral-rs/src/main.rs +++ b/node-hub/dora-mistral-rs/src/main.rs @@ -41,13 +41,13 @@ async fn main() -> eyre::Result<()> { node.send_output( mistral_output.clone(), metadata.parameters, - output.into_arrow(), + output.as_str().into_arrow(), )?; } other => eprintln!("Received input `{other}`"), }, - Event::Stop => { - println!("Received manual stop") + Event::Stop(_) => { + println!("Received command"); } Event::InputClosed { id } => { println!("input `{id}` was closed"); 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-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..f1dd9635 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" } @@ -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 }, +] 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-pytorch-kinematics/dora_pytorch_kinematics/main.py b/node-hub/dora-pytorch-kinematics/dora_pytorch_kinematics/main.py index bd5d5ec5..d1c795d4 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 + ) + 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-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/dora_qwen2_5_vl/main.py b/node-hub/dora-qwen2-5-vl/dora_qwen2_5_vl/main.py index 3125858c..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 @@ -62,29 +62,122 @@ if ADAPTER_PATH != "": processor = AutoProcessor.from_pretrained(MODEL_NAME_OR_PATH, use_fast=True) -def generate(frames: dict, question, history, past_key_values=None, image_id=None): +def generate( + frames: dict, texts: list[str], history, past_key_values=None, image_id=None +): """Generate the response to the question given the image using Qwen2 model.""" if image_id is not None: images = [frames[image_id]] else: images = list(frames.values()) - messages = [ - { - "role": "user", - "content": [ + + 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( { - "type": "image", - "image": image, - "resized_height": image.size[1] * IMAGE_RESIZE_RATIO, - "resized_width": image.size[0] * IMAGE_RESIZE_RATIO, + "role": "system", + "content": [ + {"type": "text", "text": text.replace("<|system|>\n", "")}, + ], } - for image in images - ] - + [ - {"type": "text", "text": question}, - ], - }, - ] + ) + elif text.startswith("<|assistant|>\n"): + messages.append( + { + "role": "assistant", + "content": [ + {"type": "text", "text": text.replace("<|assistant|>\n", "")}, + ], + } + ) + elif text.startswith("<|tool|>\n"): + messages.append( + { + "role": "tool", + "content": [ + {"type": "text", "text": text.replace("<|tool|>\n", "")}, + ], + } + ) + elif text.startswith("<|user|>\n<|im_start|>\n"): + messages.append( + { + "role": "user", + "content": [ + { + "type": "text", + "text": text.replace("<|user|>\n<|im_start|>\n", ""), + }, + ], + } + ) + elif text.startswith("<|user|>\n<|vision_start|>\n"): + # Handle the case where the text starts with <|user|>\n<|vision_start|> + image_url = text.replace("<|user|>\n<|vision_start|>\n", "") + + # If the last message was from the user, append the image URL to it + if messages[-1]["role"] == "user": + messages[-1]["content"].append( + { + "type": "image", + "image": image_url, + } + ) + else: + messages.append( + { + "role": "user", + "content": [ + { + "type": "image", + "image": image_url, + }, + ], + } + ) + else: + messages.append( + { + "role": "user", + "content": [ + {"type": "text", "text": text}, + ], + } + ) + + # If the last message was from the user, append the image URL to it + if messages[-1]["role"] == "user": + messages[-1]["content"] += [ + { + "type": "image", + "image": image, + "resized_height": image.size[1] * IMAGE_RESIZE_RATIO, + "resized_width": image.size[0] * IMAGE_RESIZE_RATIO, + } + for image in images + ] + else: + messages.append( + { + "role": "user", + "content": [ + { + "type": "image", + "image": image, + "resized_height": image.size[1] * IMAGE_RESIZE_RATIO, + "resized_width": image.size[0] * IMAGE_RESIZE_RATIO, + } + for image in images + ], + } + ) + tmp_history = history + messages # Preparation for inference text = processor.apply_chat_template( @@ -120,19 +213,13 @@ def generate(frames: dict, question, history, past_key_values=None, image_id=Non clean_up_tokenization_spaces=False, ) if HISTORY: - history += [ - { - "role": "user", - "content": [ - {"type": "text", "text": question}, - ], - }, + history = tmp_history + [ { "role": "assistant", "content": [ {"type": "text", "text": output_text[0]}, ], - }, + } ] return output_text[0], history, past_key_values @@ -207,24 +294,22 @@ def main(): elif "text" in event_id: if len(event["value"]) > 0: - text = event["value"][0].as_py() + texts = event["value"].to_pylist() image_id = event["metadata"].get("image_id", None) else: - text = cached_text - words = text.split() + texts = cached_text + words = texts[-1].split() if len(ACTIVATION_WORDS) > 0 and all( word not in ACTIVATION_WORDS for word in words ): continue - cached_text = text + cached_text = texts - if len(frames.keys()) == 0: - continue # set the max number of tiles in `max_num` response, history, past_key_values = generate( frames, - text, + texts, history, past_key_values, image_id, diff --git a/node-hub/dora-qwen2-5-vl/pyproject.toml b/node-hub/dora-qwen2-5-vl/pyproject.toml index 39aa23e9..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.11" +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" }, 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..b71cf93e 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" @@ -25,7 +25,8 @@ pyo3 = { workspace = true, features = [ "eyre", "generate-import-lib", ], optional = true } -avif-serialize = "0.8.3" +avif-serialize = { version = "0.8.5" } +little_exif = { version = "0.6.9" } [lib] diff --git a/node-hub/dora-rav1e/src/lib.rs b/node-hub/dora-rav1e/src/lib.rs index 22e43180..abba7c8e 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)]; @@ -107,6 +130,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 +142,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 +150,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(_) => {} @@ -159,9 +183,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 @@ -289,12 +322,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 +350,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, @@ -336,13 +366,13 @@ 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); } - 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(); @@ -370,7 +400,38 @@ 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 mut aviffy = avif_serialize::Aviffy::new(); + aviffy + .full_color_range(false) + .set_seq_profile(0) + .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(); + + node.send_output(id, metadata.parameters.clone(), arrow) + .context("could not send output") + .unwrap(); } _ => { metadata.parameters.insert( 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-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/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 0543b029..ffe763d9 100644 --- a/node-hub/dora-rerun/src/urdf.rs +++ b/node-hub/dora-rerun/src/urdf.rs @@ -60,23 +60,44 @@ 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")?; + // 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 + 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/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}`"), - }, - _ => {} + } } } 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-vggt/dora_vggt/main.py b/node-hub/dora-vggt/dora_vggt/main.py index 7c0e24c7..e494c8e3 100644 --- a/node-hub/dora-vggt/dora_vggt/main.py +++ b/node-hub/dora-vggt/dora_vggt/main.py @@ -1,7 +1,8 @@ """TODO: Add docstring.""" - import io -from collections import deque as Deque +import os +from collections import deque + import cv2 import numpy as np @@ -13,26 +14,31 @@ 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 +# 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 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": - if "image" in event["id"]: storage = event["value"] metadata = event["metadata"] @@ -80,7 +86,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) @@ -88,7 +94,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] @@ -98,29 +104,32 @@ def main(): # Predict Depth Maps depth_map, depth_conf = model.depth_head( - aggregated_tokens_list, images, ps_idx + 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. + if DEPTH_ENCODING == "mono16": + depth_map = (depth_map * 1000).astype(np.uint16) + 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], "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), + ], }, ) @@ -129,18 +138,22 @@ 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", "width": image.shape[1], "height": image.shape[0], + "focal": [ + int(f_0), + int(f_1), + ], + "resolution": [ + int(r_0), + int(r_1), + ], }, ) 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/gamepad/main.py b/node-hub/gamepad/gamepad/main.py index ad6385ea..6dd61280 100644 --- a/node-hub/gamepad/gamepad/main.py +++ b/node-hub/gamepad/gamepad/main.py @@ -1,149 +1,152 @@ """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 +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.""" 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 = JOYSTICK_DEADZONE + + # Linear X velocity from D-pad vertical + linear_x = 0.0 + if dpad_y != 0: + 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 * 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_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() 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/openai-proxy-server/src/main.rs b/node-hub/openai-proxy-server/src/main.rs index c0714886..4cee442f 100644 --- a/node-hub/openai-proxy-server/src/main.rs +++ b/node-hub/openai-proxy-server/src/main.rs @@ -1,4 +1,10 @@ -use dora_node_api::{self, dora_core::config::DataId, merged::MergeExternalSend, DoraNode, Event}; +use dora_node_api::{ + self, + arrow::array::{AsArray, StringArray}, + dora_core::config::DataId, + merged::MergeExternalSend, + DoraNode, Event, +}; use eyre::{Context, ContextCompat}; use futures::{ @@ -14,7 +20,7 @@ use hyper::{ }; use message::{ ChatCompletionObject, ChatCompletionObjectChoice, ChatCompletionObjectMessage, - ChatCompletionRequest, ChatCompletionRequestMessage, Usage, + ChatCompletionRequest, Usage, }; use std::{ collections::VecDeque, @@ -71,7 +77,7 @@ async fn main() -> eyre::Result<()> { let merged = events.merge_external_send(server_events); let events = futures::executor::block_on_stream(merged); - let output_id = DataId::from("chat_completion_request".to_owned()); + let output_id = DataId::from("text".to_owned()); let mut reply_channels = VecDeque::new(); for event in events { @@ -82,45 +88,15 @@ async fn main() -> eyre::Result<()> { break; } ServerEvent::ChatCompletionRequest { request, reply } => { - let message = request - .messages - .into_iter() - .find_map(|m| match m { - ChatCompletionRequestMessage::User(message) => Some(message), - _ => None, - }) - .context("no user message found"); - match message { - Ok(message) => match message.content() { - message::ChatCompletionUserMessageContent::Text(content) => { - node.send_output_bytes( - output_id.clone(), - Default::default(), - content.len(), - content.as_bytes(), - ) - .context("failed to send dora output")?; - reply_channels.push_back(( - reply, - content.as_bytes().len() as u64, - request.model, - )); - } - message::ChatCompletionUserMessageContent::Parts(_) => { - if reply - .send(Err(eyre::eyre!("unsupported message content"))) - .is_err() - { - tracing::warn!("failed to send chat completion reply because channel closed early"); - }; - } - }, - Err(err) => { - if reply.send(Err(err)).is_err() { - tracing::warn!("failed to send chat completion reply error because channel closed early"); - } - } - } + let texts = request.to_texts(); + node.send_output( + output_id.clone(), + Default::default(), + StringArray::from(texts), + ) + .context("failed to send dora output")?; + + reply_channels.push_back((reply, 0_u64, request.model)); } }, dora_node_api::merged::MergedEvent::Dora(event) => match event { @@ -130,46 +106,56 @@ async fn main() -> eyre::Result<()> { metadata: _, } => { match id.as_str() { - "completion_reply" => { + "text" => { let (reply_channel, prompt_tokens, model) = reply_channels.pop_front().context("no reply channel")?; - let data = TryFrom::try_from(&data) - .with_context(|| format!("invalid reply data: {data:?}")) - .map(|s: &[u8]| ChatCompletionObject { - id: format!("completion-{}", uuid::Uuid::new_v4()), - object: "chat.completion".to_string(), - created: chrono::Utc::now().timestamp() as u64, - model: model.unwrap_or_default(), - choices: vec![ChatCompletionObjectChoice { - index: 0, - message: ChatCompletionObjectMessage { - role: message::ChatCompletionRole::Assistant, - content: Some(String::from_utf8_lossy(s).to_string()), - tool_calls: Vec::new(), - function_call: None, - }, - finish_reason: message::FinishReason::stop, - logprobs: None, - }], - usage: Usage { - prompt_tokens, - completion_tokens: s.len() as u64, - total_tokens: prompt_tokens + s.len() as u64, + let data = data.as_string::(); + let string = data.iter().fold("".to_string(), |mut acc, s| { + if let Some(s) = s { + acc.push('\n'); + acc.push_str(s); + } + acc + }); + + let data = ChatCompletionObject { + id: format!("completion-{}", uuid::Uuid::new_v4()), + object: "chat.completion".to_string(), + created: chrono::Utc::now().timestamp() as u64, + model: model.unwrap_or_default(), + choices: vec![ChatCompletionObjectChoice { + index: 0, + message: ChatCompletionObjectMessage { + role: message::ChatCompletionRole::Assistant, + content: Some(string.to_string()), + tool_calls: Vec::new(), + function_call: None, }, - }); - - if reply_channel.send(data).is_err() { + finish_reason: message::FinishReason::stop, + logprobs: None, + }], + usage: Usage { + prompt_tokens, + completion_tokens: string.len() as u64, + total_tokens: prompt_tokens + string.len() as u64, + }, + }; + + if reply_channel.send(Ok(data)).is_err() { tracing::warn!("failed to send chat completion reply because channel closed early"); } } _ => eyre::bail!("unexpected input id: {}", id), }; } - Event::Stop => { + Event::Stop(_) => { break; } + Event::InputClosed { id, .. } => { + info!("Input channel closed for id: {}", id); + } event => { - println!("Event: {event:#?}") + eyre::bail!("unexpected event: {:#?}", event) } }, } @@ -178,6 +164,7 @@ async fn main() -> eyre::Result<()> { Ok(()) } +#[allow(clippy::large_enum_variant)] enum ServerEvent { Result(eyre::Result<()>), ChatCompletionRequest { diff --git a/node-hub/openai-proxy-server/src/message.rs b/node-hub/openai-proxy-server/src/message.rs index dff7e101..4c9eb99f 100644 --- a/node-hub/openai-proxy-server/src/message.rs +++ b/node-hub/openai-proxy-server/src/message.rs @@ -230,6 +230,15 @@ impl<'de> Deserialize<'de> for ChatCompletionRequest { } } +impl ChatCompletionRequest { + pub fn to_texts(&self) -> Vec { + self.messages + .iter() + .flat_map(|message| message.to_texts()) + .collect() + } +} + /// Message for comprising the conversation. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(tag = "role", rename_all = "lowercase")] @@ -308,6 +317,22 @@ impl ChatCompletionRequestMessage { ChatCompletionRequestMessage::Tool(_) => None, } } + + /// The contents of the message. + pub fn to_texts(&self) -> Vec { + match self { + ChatCompletionRequestMessage::System(message) => { + vec![String::from("<|system|>\n") + &message.content] + } + ChatCompletionRequestMessage::User(message) => message.content.to_texts(), + ChatCompletionRequestMessage::Assistant(message) => { + vec![String::from("<|assistant|>\n") + &message.content.clone().unwrap_or_default()] + } + ChatCompletionRequestMessage::Tool(message) => { + vec![String::from("<|tool|>\n") + &message.content.clone()] + } + } + } } /// Sampling methods used for chat completion requests. @@ -587,6 +612,25 @@ impl ChatCompletionUserMessageContent { ChatCompletionUserMessageContent::Parts(_) => "parts", } } + + pub fn to_texts(&self) -> Vec { + match self { + ChatCompletionUserMessageContent::Text(text) => { + vec![String::from("user: ") + &text.clone()] + } + ChatCompletionUserMessageContent::Parts(parts) => parts + .iter() + .map(|part| match part { + ContentPart::Text(text_part) => { + String::from("<|user|>\n<|im_start|>\n") + &text_part.text.clone() + } + ContentPart::Image(image) => { + String::from("<|user|>\n<|vision_start|>\n") + &image.image().url.clone() + } + }) + .collect(), + } + } } /// Define the content part of a user message. 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" },