From 926569d3d0837c57cbefd2f174c43e6c518b6f96 Mon Sep 17 00:00:00 2001 From: "randomizedcoder dave.seddon.ca@gmail.com" Date: Fri, 24 Apr 2026 16:05:31 -0700 Subject: [PATCH 1/3] feat(nix): add modular Nix flake with demos, OCI containers, and model catalog Add reproducible Nix packaging for larql: - flake.nix orchestrator importing modular files from nix/ - package.nix: Rust derivation with cargoHash, system protoc via patch - shell.nix: dev shell with Rust tools, Python/maturin, debuggers - container.nix: OCI images for larql-server and larql CLI (Linux) - models.nix: 8-model HuggingFace catalog (SmolLM2, Qwen, TinyLlama, StableLM, Phi-3.5, Gemma 4 E2B/E4B) fetched as fixed-output derivations - demo.nix: per-model walk demos and interactive REPL targets - Patches: remove protobuf-src (use nixpkgs protoc), disable default metal feature (macOS-only), add cfg guard to bench_cmd.rs - README.md: install guide, remote usage from GitHub, model docs - Commit Cargo.lock (required by buildRustPackage) Co-Authored-By: Claude Opus 4.6 --- .gitignore | 5 +- Cargo.lock | 5511 +++++++++++++++++++++++++++ flake.lock | 61 + flake.nix | 73 + nix/README.md | 391 ++ nix/banner.nix | 28 + nix/container.nix | 131 + nix/demo.nix | 168 + nix/example-queries.md | 42 + nix/models.nix | 100 + nix/package.nix | 90 + nix/patches/use-system-protoc.patch | 71 + nix/shell.nix | 38 + 13 files changed, 6708 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix/README.md create mode 100644 nix/banner.nix create mode 100644 nix/container.nix create mode 100644 nix/demo.nix create mode 100644 nix/example-queries.md create mode 100644 nix/models.nix create mode 100644 nix/package.nix create mode 100644 nix/patches/use-system-protoc.patch create mode 100644 nix/shell.nix diff --git a/.gitignore b/.gitignore index ced35f88d..2b7076bd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Rust target/ **/*.rs.bk -Cargo.lock # Python / maturin .venv/ @@ -27,6 +26,10 @@ build/ # Checkpoints / temp *.checkpoint +# Nix +result +result-* + # output output/ data/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..001056847 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,5511 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "accelerate-src" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "415ed64958754dbe991900f3940677e6a7eefb4d7367afd70d642677b0c7d19d" + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object 0.37.3", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] + +[[package]] +name = "assert_approx_eq" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-rs" +version = "1.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "base64 0.22.1", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-server" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ab4a3ec9ea8a657c72d99a03a824af695bd0fb5ec639ccbd9cd3543b41a5f9" +dependencies = [ + "arc-swap", + "bytes", + "fs-err", + "http", + "http-body", + "hyper", + "hyper-util", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "blas-src" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95e83dc868db96e69795c0213143095f03de9dd3252f205d4ac716e4076a7e0" +dependencies = [ + "accelerate-src", + "openblas-src", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cap-fs-ext" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5528f85b1e134ae811704e41ef80930f56e795923f866813255bc342cc20654" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.59.0", +] + +[[package]] +name = "cap-net-ext" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20a158160765c6a7d0d8c072a53d772e4cb243f38b04bfcf6b4939cfbe7482e7" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix 1.1.4", + "smallvec", +] + +[[package]] +name = "cap-primitives" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cf3aea8a5081171859ef57bc1606b1df6999df4f1110f8eef68b30098d1d3a" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix 1.1.4", + "rustix-linux-procfs", + "windows-sys 0.59.0", + "winx", +] + +[[package]] +name = "cap-rand" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8144c22e24bbcf26ade86cb6501a0916c46b7e4787abdb0045a467eb1645a1d" +dependencies = [ + "ambient-authority", + "rand 0.8.6", +] + +[[package]] +name = "cap-std" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6dc3090992a735d23219de5c204927163d922f42f575a0189b005c62d37549a" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix 1.1.4", +] + +[[package]] +name = "cap-time-ext" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def102506ce40c11710a9b16e614af0cde8e76ae51b1f48c04b8d79f4b671a80" +dependencies = [ + "ambient-authority", + "cap-primitives", + "iana-time-zone", + "once_cell", + "rustix 1.1.4", + "winx", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cblas-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6feecd82cce51b0204cf063f0041d69f24ce83f680d87514b004248e7b0fa65" +dependencies = [ + "libc", +] + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.18", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", +] + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "unicode-width", + "windows-sys 0.61.2", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e15d04a0ce86cb36ead88ad68cf693ffd6cda47052b9e0ac114bc47fd9cd23c4" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-bitset" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c6e3969a7ce267259ce244b7867c5d3bc9e65b0a87e81039588dfdeaede9f34" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-codegen" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c22032c4cb42558371cf516bb47f26cdad1819d3475c133e93c49f50ebf304e" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.14.5", + "log", + "regalloc2", + "rustc-hash", + "serde", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904bc71c61b27fc57827f4a1379f29de64fe95653b620a3db77d59655eee0b8" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40180f5497572f644ce88c255480981ae2ec1d7bb4d8e0c0136a13b87a2f2ceb" + +[[package]] +name = "cranelift-control" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d132c6d0bd8a489563472afc171759da0707804a65ece7ceb15a8c6d7dd5ef" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d0d9618275474fbf679dd018ac6e009acbd6ae6850f6a67be33fb3b00b323" +dependencies = [ + "cranelift-bitset", + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-frontend" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fac41e16729107393174b0c9e3730fb072866100e1e64e80a1a963b2e484d57" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca20d576e5070044d0a72a9effc2deacf4d6aa650403189d8ea50126483944d" + +[[package]] +name = "cranelift-native" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dary_heap" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1e3a325bc115f096c8b77bbf027a7c2592230e70be2d985be950d3d5e60ebe" +dependencies = [ + "serde", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +dependencies = [ + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[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.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "esaxx-rs" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6" +dependencies = [ + "cc", +] + +[[package]] +name = "evalexpr" +version = "12.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae893d2d5e908b78f151ed89de3bfc272cdf6d368c7ed866942f98e24dea208a" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix 1.1.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-err" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fde052dbfc920003cfd2c8e2c6e6d4cc7c1091538c3a24226cec0665ab08c0" +dependencies = [ + "autocfg", + "tokio", +] + +[[package]] +name = "fs-set-times" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" +dependencies = [ + "io-lifetimes", + "rustix 1.1.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", + "serde", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hf-hub" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef3982638978efa195ff11b305f51f1f22f4f0a6cabee7af79b383ebee6a213" +dependencies = [ + "dirs 6.0.0", + "futures", + "http", + "indicatif 0.18.4", + "libc", + "log", + "native-tls", + "num_cpus", + "rand 0.9.4", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "ureq", + "windows-sys 0.61.2", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.3", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console 0.15.11", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + +[[package]] +name = "indicatif" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" +dependencies = [ + "console 0.16.3", + "portable-atomic", + "unicode-width", + "unit-prefix", + "web-time", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "io-extras" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" +dependencies = [ + "io-lifetimes", + "windows-sys 0.59.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kv-cache-benchmark" +version = "0.1.0" +dependencies = [ + "criterion", + "larql-compute", + "larql-inference", + "larql-models", + "larql-vindex", + "ndarray", + "rand 0.8.6", + "rand_distr", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokenizers", + "zip", +] + +[[package]] +name = "larql-cli" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "clap", + "indicatif 0.17.11", + "larql-compute", + "larql-core", + "larql-inference", + "larql-lql", + "larql-models", + "larql-vindex", + "libc", + "memmap2", + "minijinja", + "ndarray", + "reqwest", + "safetensors", + "serde", + "serde_json", + "tempfile", + "thiserror 2.0.18", + "tokenizers", +] + +[[package]] +name = "larql-compute" +version = "0.1.0" +dependencies = [ + "blas-src", + "cc", + "criterion", + "larql-models", + "libc", + "memmap2", + "metal", + "ndarray", + "openblas-src", + "serde_json", +] + +[[package]] +name = "larql-core" +version = "0.1.0" +dependencies = [ + "assert_approx_eq", + "reqwest", + "rmp-serde", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "larql-inference" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_approx_eq", + "blas-src", + "larql-compute", + "larql-core", + "larql-models", + "larql-vindex", + "libc", + "memmap2", + "ndarray", + "openblas-src", + "rayon", + "reqwest", + "safetensors", + "serde", + "serde_json", + "tempfile", + "thiserror 2.0.18", + "tokenizers", + "wasmtime", + "wasmtime-wasi", +] + +[[package]] +name = "larql-lql" +version = "0.1.0" +dependencies = [ + "criterion", + "larql-compute", + "larql-core", + "larql-inference", + "larql-models", + "larql-vindex", + "ndarray", + "reqwest", + "rustyline", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "larql-models" +version = "0.1.0" +dependencies = [ + "memmap2", + "ndarray", + "safetensors", + "serde", + "serde_json", + "tempfile", + "thiserror 2.0.18", +] + +[[package]] +name = "larql-python" +version = "0.1.0" +dependencies = [ + "larql-core", + "larql-inference", + "larql-lql", + "larql-models", + "larql-vindex", + "memmap2", + "ndarray", + "numpy", + "pyo3", + "serde", + "serde_json", +] + +[[package]] +name = "larql-router" +version = "0.1.0" +dependencies = [ + "axum", + "clap", + "futures", + "futures-core", + "larql-router-protocol", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tonic", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "larql-router-protocol" +version = "0.1.0" +dependencies = [ + "prost", + "protobuf-src", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", +] + +[[package]] +name = "larql-server" +version = "0.1.0" +dependencies = [ + "axum", + "axum-server", + "clap", + "larql-compute", + "larql-inference", + "larql-models", + "larql-router-protocol", + "larql-vindex", + "memmap2", + "prost", + "protobuf-src", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "larql-vindex" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "criterion", + "hf-hub", + "larql-compute", + "larql-models", + "libc", + "memmap2", + "ndarray", + "rayon", + "reqwest", + "safetensors", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tokenizers", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cc46bac87ef8093eed6f272babb833b6443374399985ac8ed28471ee0918545" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "bitflags 2.11.1", + "libc", + "plain", + "redox_syscall 0.7.4", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "macro_rules_attribute" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memfd" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +dependencies = [ + "rustix 1.1.4", +] + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +dependencies = [ + "bitflags 2.11.1", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minijinja" +version = "2.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805bfd7352166bae857ee569628b52bcd85a1cecf7810861ebceb1686b72b75d" +dependencies = [ + "memo-map", + "serde", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "model-compute" +version = "0.1.0" +dependencies = [ + "chrono", + "criterion", + "evalexpr", + "thiserror 2.0.18", + "wasmtime", + "wat", +] + +[[package]] +name = "monostate" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3341a273f6c9d5bef1908f17b7267bbab0e95c9bf69a0d4dcf8e9e1b2c76ef67" +dependencies = [ + "monostate-impl", + "serde", + "serde_core", +] + +[[package]] +name = "monostate-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4db6d5580af57bf992f59068d4ea26fd518574ff48d7639b255a36f9de6e7e9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndarray" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" +dependencies = [ + "cblas-sys", + "libc", + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "numpy" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cfbf3f0feededcaa4d289fe3079b03659e85c5b5a177f4ba6fb01ab4fb3e39" +dependencies = [ + "libc", + "ndarray", + "num-complex", + "num-integer", + "num-traits", + "pyo3", + "pyo3-build-config", + "rustc-hash", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "crc32fast", + "hashbrown 0.15.5", + "indexmap", + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "onig" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" +dependencies = [ + "bitflags 2.11.1", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "openblas-build" +version = "0.10.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd235aa8876fa5c4be452efde09b9b8bafa19aea0bf14a4926508213082439a3" +dependencies = [ + "anyhow", + "cc", + "flate2", + "tar", + "thiserror 2.0.18", + "ureq", +] + +[[package]] +name = "openblas-src" +version = "0.10.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fccd2c4f5271ab871f2069cb6f1a13ef2c0db50e1145ce03428ee541f4c63c4f" +dependencies = [ + "dirs 6.0.0", + "openblas-build", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "openssl" +version = "0.10.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools 0.14.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "protobuf-src" +version = "2.1.1+27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6217c3504da19b85a3a4b2e9a5183d635822d83507ba0986624b5c05b83bfc40" +dependencies = [ + "cmake", +] + +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "pulley-interpreter" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d95f8575df49a2708398182f49a888cf9dc30210fb1fd2df87c889edcee75d" +dependencies = [ + "cranelift-bitset", + "log", + "sptr", + "wasmtime-math", +] + +[[package]] +name = "pyo3" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.6", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-cond" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2964d0cf57a3e7a06e8183d14a8b527195c706b7983549cd5462d5aa3747438f" +dependencies = [ + "either", + "itertools 0.14.0", + "rayon", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_syscall" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "regalloc2" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc06e6b318142614e4a48bc725abbf08ff166694835c43c9dae5a9009704639a" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.15.5", + "log", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustix-linux-procfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056" +dependencies = [ + "once_cell", + "rustix 1.1.4", +] + +[[package]] +name = "rustls" +version = "0.23.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rustyline" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "windows-sys 0.59.0", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "safetensors" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc0cdb7198d738a111f6df8fef42cb175412c311d0c4ac9126ff4e550ad1a0e8" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs 4.0.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "spm_precompiled" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5851699c4033c63636f7ea4cf7b7c1f1bf06d0cc03cfb42e711de5a5c46cf326" +dependencies = [ + "base64 0.13.1", + "nom", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-interface" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4592f674ce18521c2a81483873a49596655b179f71c5e05d10c1fe66c78745" +dependencies = [ + "bitflags 2.11.1", + "cap-fs-ext", + "cap-std", + "fd-lock", + "io-lifetimes", + "rustix 0.38.44", + "windows-sys 0.59.0", + "winx", +] + +[[package]] +name = "tar" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokenizers" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a620b996116a59e184c2fa2dfd8251ea34a36d0a514758c6f966386bd2e03476" +dependencies = [ + "ahash", + "aho-corasick", + "compact_str", + "dary_heap", + "derive_builder", + "esaxx-rs", + "getrandom 0.3.4", + "indicatif 0.17.11", + "itertools 0.14.0", + "log", + "macro_rules_attribute", + "monostate", + "onig", + "paste", + "rand 0.9.4", + "rayon", + "rayon-cond", + "regex", + "regex-syntax", + "serde", + "serde_json", + "spm_precompiled", + "thiserror 2.0.18", + "unicode-normalization-alignments", + "unicode-segmentation", + "unicode_categories", +] + +[[package]] +name = "tokio" +version = "1.52.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2 0.5.10", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac6f67be712d12f0b41328db3137e0d0757645d8904b4cb7d51cd9c2279e847" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.11.1", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror 2.0.18", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization-alignments" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f613e4fa046e69818dd287fdc4bc78175ff20331479dab6e1b0f98d57062de" +dependencies = [ + "smallvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +dependencies = [ + "base64 0.22.1", + "cookie_store", + "der", + "flate2", + "log", + "native-tls", + "percent-encoding", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "socks", + "ureq-proto", + "utf8-zero", + "webpki-root-certs", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +dependencies = [ + "base64 0.22.1", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.221.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc8444fe4920de80a4fe5ab564fff2ae58b6b73166b89751f8c6c93509da32e5" +dependencies = [ + "leb128", + "wasmparser 0.221.3", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b6733b8b91d010a6ac5b0fb237dc46a19650bc4c67db66857e2e787d437204" +dependencies = [ + "leb128fmt", + "wasmparser 0.247.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.221.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06bfa36ab3ac2be0dee563380147a5b81ba10dd8885d7fbbc9eb574be67d185" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.15.5", + "indexmap", + "semver", + "serde", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80" +dependencies = [ + "bitflags 2.11.1", + "indexmap", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.221.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7343c42a97f2926c7819ff81b64012092ae954c5d83ddd30c9fcdefd97d0b283" +dependencies = [ + "anyhow", + "termcolor", + "wasmparser 0.221.3", +] + +[[package]] +name = "wasmtime" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11976a250672556d1c4c04c6d5d7656ac9192ac9edc42a4587d6c21460010e69" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.11.1", + "bumpalo", + "cc", + "cfg-if", + "encoding_rs", + "hashbrown 0.14.5", + "indexmap", + "libc", + "log", + "mach2", + "memfd", + "object 0.36.7", + "once_cell", + "paste", + "postcard", + "psm", + "pulley-interpreter", + "rustix 0.38.44", + "semver", + "serde", + "serde_derive", + "smallvec", + "sptr", + "target-lexicon", + "trait-variant", + "wasmparser 0.221.3", + "wasmtime-asm-macros", + "wasmtime-component-macro", + "wasmtime-component-util", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit-icache-coherence", + "wasmtime-math", + "wasmtime-slab", + "wasmtime-versioned-export-macros", + "wasmtime-winch", + "windows-sys 0.59.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f178b0d125201fbe9f75beaf849bd3e511891f9e45ba216a5b620802ccf64f2" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-component-macro" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d74de6592ed945d0a602f71243982a304d5d02f1e501b638addf57f42d57dfaf" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wasmtime-component-util", + "wasmtime-wit-bindgen", + "wit-parser 0.221.3", +] + +[[package]] +name = "wasmtime-component-util" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707dc7b3c112ab5a366b30cfe2fb5b2f8e6a0f682f16df96a5ec582bfe6f056e" + +[[package]] +name = "wasmtime-cranelift" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366be722674d4bf153290fbcbc4d7d16895cc82fb3e869f8d550ff768f9e9e87" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "gimli", + "itertools 0.12.1", + "log", + "object 0.36.7", + "smallvec", + "target-lexicon", + "thiserror 1.0.69", + "wasmparser 0.221.3", + "wasmtime-environ", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-environ" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdadc1af7097347aa276a4f008929810f726b5b46946971c660b6d421e9994ad" +dependencies = [ + "anyhow", + "cranelift-bitset", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "object 0.36.7", + "postcard", + "semver", + "serde", + "serde_derive", + "smallvec", + "target-lexicon", + "wasm-encoder 0.221.3", + "wasmparser 0.221.3", + "wasmprinter", + "wasmtime-component-util", +] + +[[package]] +name = "wasmtime-fiber" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccba90d4119f081bca91190485650730a617be1fff5228f8c4757ce133d21117" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "rustix 0.38.44", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec5e8552e01692e6c2e5293171704fed8abdec79d1a6995a0870ab190e5747d1" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "wasmtime-math" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29210ec2aa25e00f4d54605cedaf080f39ec01a872c5bd520ad04c67af1dde17" +dependencies = [ + "libm", +] + +[[package]] +name = "wasmtime-slab" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb5821a96fa04ac14bc7b158bb3d5cd7729a053db5a74dad396cd513a5e5ccf" + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ff86db216dc0240462de40c8290887a613dddf9685508eb39479037ba97b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmtime-wasi" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d1be69bfcab1bdac74daa7a1f9695ab992b9c8e21b9b061e7d66434097e0ca4" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.11.1", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "futures", + "io-extras", + "io-lifetimes", + "rustix 0.38.44", + "system-interface", + "thiserror 1.0.69", + "tokio", + "tracing", + "trait-variant", + "url", + "wasmtime", + "wiggle", + "windows-sys 0.59.0", +] + +[[package]] +name = "wasmtime-winch" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdbabfb8f20502d5e1d81092b9ead3682ae59988487aafcd7567387b7a43cf8f" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "object 0.36.7", + "target-lexicon", + "wasmparser 0.221.3", + "wasmtime-cranelift", + "wasmtime-environ", + "winch-codegen", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8358319c2dd1e4db79e3c1c5d3a5af84956615343f9f89f4e4996a36816e06e6" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "wit-parser 0.221.3", +] + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "247.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579d2d47eb33b0cdf9b14723cb115f1e1b7d6e77aac6f0816e5b7c7aeaa418ff" +dependencies = [ + "bumpalo", + "leb128fmt", + "memchr", + "unicode-width", + "wasm-encoder 0.247.0", +] + +[[package]] +name = "wat" +version = "1.247.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f4091c56437e86f2b57fa2fac72c4f528957a605b3f44f7c0b3b19a17ac5ee" +dependencies = [ + "wast 247.0.0", +] + +[[package]] +name = "web-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "wiggle" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9af35bc9629c52c261465320a9a07959164928b4241980ba1cf923b9e6751d" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.11.1", + "thiserror 1.0.69", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf267dd05673912c8138f4b54acabe6bd53407d9d1536f0fadb6520dd16e101" +dependencies = [ + "anyhow", + "heck", + "proc-macro2", + "quote", + "shellexpand", + "syn", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c5c473d4198e6c2d377f3809f713ff0c110cab88a0805ae099a82119ee250c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wiggle-generate", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winch-codegen" +version = "29.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f849ef2c5f46cb0a20af4b4487aaa239846e52e2c03f13fa3c784684552859c" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "regalloc2", + "smallvec", + "target-lexicon", + "thiserror 1.0.69", + "wasmparser 0.221.3", + "wasmtime-cranelift", + "wasmtime-environ", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winx" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" +dependencies = [ + "bitflags 2.11.1", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.244.0", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.1", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.244.0", + "wasm-metadata", + "wasmparser 0.244.0", + "wit-parser 0.244.0", +] + +[[package]] +name = "wit-parser" +version = "0.221.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896112579ed56b4a538b07a3d16e562d101ff6265c46b515ce0c701eef16b2ac" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.221.3", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.244.0", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror 1.0.69", + "wast 35.0.2", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.4", +] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "indexmap", + "memchr", + "thiserror 2.0.18", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..c8abda9e6 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1776548001, + "narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..f8b37ee18 --- /dev/null +++ b/flake.nix @@ -0,0 +1,73 @@ +# +# flake.nix - LARQL Nix packaging +# +# Quick Start: +# nix build # Build larql-cli binary +# nix develop # Development shell +# nix flake show # List all outputs +# +# Demo (models fetched via git-lfs, vindexes built at nix-build time): +# nix run .#demo-list # List available models +# nix run .#demo # Walk default model (gemma4-4b) +# nix run .#demo- # Walk a specific model +# nix run .#demo-info # Show GGUF metadata for local models +# nix build .#vindex- # Build a vindex from a model +# +# OCI Containers (Linux only): +# nix build .#container # larql-server image +# nix build .#container-cli # larql CLI image +# docker load < result +# +# See also: ./nix/README.md +# +{ + description = "LARQL - query engine for transformer model weights"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + nixDir = ./nix; + pkgs = nixpkgs.legacyPackages.${system}; + lib = pkgs.lib; + + # Import modular package definition + larql = import (nixDir + "/package.nix") { inherit pkgs lib; src = self; }; + + # Import OCI container images (Linux only) + containers = lib.optionalAttrs pkgs.stdenv.isLinux ( + import (nixDir + "/container.nix") { inherit pkgs lib larql; } + ); + + # Import demo (model fetch + vindex extraction + apps) + demo = import (nixDir + "/demo.nix") { inherit pkgs lib larql; }; + in + { + packages = { + default = larql; + inherit larql; + } // lib.optionalAttrs pkgs.stdenv.isLinux { + container = containers.server; + container-cli = containers.cli; + } + # Demo packages (model + vindex) + // demo.packages; + + # Demo and utility apps + apps = demo.apps; + + # Import modular development shell + devShells.default = import (nixDir + "/shell.nix") { inherit pkgs lib larql; }; + } + ); +} diff --git a/nix/README.md b/nix/README.md new file mode 100644 index 000000000..039d2ff2d --- /dev/null +++ b/nix/README.md @@ -0,0 +1,391 @@ +# LARQL — Nix Flake Development Environment + +This document explains how to use **Nix** to set up a reproducible development environment for the **LARQL** project. + +## Goals + +The goals of using Nix in this repository are to: +- **Simplify onboarding** — make it easy to get started with LARQL on any Linux or macOS system +- **Improve reproducibility** — ensure consistent build environments across developers (no more "it worked on my machine") +- **Reduce setup friction** — eliminate dependency conflicts and version mismatches (Rust toolchain, OpenBLAS, protobuf, etc.) + +Feedback and pull requests are welcome. If we're missing a tool, please open an issue or PR. See `nix/package.nix` for package definitions and `nix/shell.nix` for development tools. + +--- + +## Table of Contents + +- [LARQL — Nix Flake Development Environment](#larql--nix-flake-development-environment) + - [Goals](#goals) + - [Background](#background) + - [Quick Start](#quick-start) + - [1. Install Nix](#1-install-nix) + - [2. Enter Development Environment](#2-enter-development-environment) + - [3. First Run Considerations](#3-first-run-considerations) + - [4. Build and Test](#4-build-and-test) + - [File Structure](#file-structure) + - [Common Commands](#common-commands) + - [Python Bindings](#python-bindings) + - [Notes](#notes) + - [Troubleshooting](#troubleshooting) + +--- + +## Background + +[Nix](https://nixos.org) is a package manager for Linux and other Unix-like systems that provides **reproducible, isolated** environments. By tracking all dependencies and hashing their content, it ensures every developer uses the same versions of every package. + +### What This Repository Provides + +This repository includes `flake.nix`, `flake.lock`, and modular Nix files in `nix/`: + +- **`flake.nix`** — Main entry point; imports modules from `nix/` and wires up the package and development shell +- **`flake.lock`** — Pins exact versions so all developers use **identical** inputs +- **`nix/package.nix`** — Rust package derivation (`rustPlatform.buildRustPackage`) +- **`nix/shell.nix`** — Development shell configuration (Rust tools, Python, debuggers) +- **`nix/README.md`** — This file + +Running `nix develop` spawns a shell with the correct Rust toolchain, system libraries (OpenBLAS, protobuf, OpenSSL), and development tools configured for you. + +### Current Toolchain Versions + +The Nix development environment provides the following (from nixpkgs unstable): + +| Package | Purpose | +|---------|---------| +| Rust (~1.92) | Compiler and cargo (well above `rust-version = "1.75"` minimum) | +| OpenBLAS | BLAS backend for `ndarray` (Linux) | +| Accelerate | BLAS backend (macOS, via Apple framework) | +| protobuf | gRPC code generation for `larql-server` | +| OpenSSL | TLS for HTTP clients (`reqwest`) | +| rust-analyzer | LSP server for IDE integration | +| clippy / rustfmt | Linting and formatting | +| cargo-watch | Auto-rebuild on file changes | +| Python 3 + pytest + maturin | PyO3 bindings development | + +--- + +## Quick Start + +Install Nix if you don't already have it, then enter the development environment. + +### 1. Install Nix + +Choose **multi-user** (daemon) or **single-user**: + +- **Multi-user install** (recommended on most distros) + [Install Nix (multi-user)](https://nix.dev/manual/nix/2.24/installation/#multi-user) + ```bash + bash <(curl -L https://nixos.org/nix/install) --daemon + ``` + +- **Single-user install** + [Install Nix (single-user)](https://nix.dev/manual/nix/2.24/installation/#single-user) + ```bash + bash <(curl -L https://nixos.org/nix/install) --no-daemon + ``` + +#### Video Tutorials + +| Platform | Video | +|----------|-------| +| Ubuntu | [Installing Nix on Ubuntu](https://youtu.be/cb7BBZLhuUY) | +| Fedora | [Installing Nix on Fedora](https://youtu.be/RvaTxMa4IiY) | + +### 2. Enter Development Environment + +#### Enable Flakes (if needed) + +If you don't have the "flakes" feature enabled, run this command: +```bash +nix --extra-experimental-features 'nix-command flakes' develop . +``` + +To permanently enable the Nix "flakes" feature, update `/etc/nix/nix.conf`: +```bash +test -d /etc/nix || sudo mkdir /etc/nix +echo 'experimental-features = nix-command flakes' | sudo tee -a /etc/nix/nix.conf +``` + +With flakes enabled, simply run: +```bash +nix develop +``` + +See also: [Nix Flakes Wiki](https://nixos.wiki/wiki/flakes) + +### 3. First Run Considerations + +On first execution, Nix will download and build all dependencies, which might take several minutes. On subsequent executions, Nix will reuse the cache in `/nix/store/` and will be essentially instantaneous. + +> **Note:** Nix will not interact with any "system" packages you may already have installed. The Nix versions are isolated and will effectively "disappear" when you exit the development shell. + +### 4. Build and Test + +Once inside the Nix development shell: + +```bash +# Build the larql CLI binary +cargo build --release + +# Run all workspace tests +cargo test + +# Run the full CI check (fmt + clippy + test) +make ci + +# Or build via Nix directly (outside the shell) +nix build +./result/bin/larql --help +``` + +--- + +## Remote Usage (no clone needed) + +Nix flakes can be run directly from GitHub — no need to clone the repository. This is useful for trying out LARQL or running demos on any machine with Nix installed. + +### From the main repository + +Once the Nix flake is merged into `main`: + +```bash +# Build the larql binary +nix build github:chrishayuk/larql + +# Enter the development shell +nix develop github:chrishayuk/larql + +# List available demo models +nix run github:chrishayuk/larql#demo-list + +# Run the default demo (gemma4-4b) +nix run github:chrishayuk/larql#demo + +# Run a specific model demo +nix run github:chrishayuk/larql#demo-tinyllama-1b + +# Launch the interactive REPL with a model +nix run github:chrishayuk/larql#repl + +# Run with a custom prompt +nix run github:chrishayuk/larql#demo-qwen-15b -- "The meaning of life is" +``` + +### From a fork or branch + +Before the PR is merged, you can run from a fork or a specific branch: + +```bash +# From a fork (e.g. randomizedcoder's fork, nix branch) +nix run github:randomizedcoder/larql/nix#demo-list +nix run github:randomizedcoder/larql/nix#repl +nix develop github:randomizedcoder/larql/nix + +# General syntax: github://# +nix run github://#demo +``` + +### Pinning a specific revision + +For reproducibility, you can pin to an exact commit: + +```bash +nix run github:chrishayuk/larql/#demo +``` + +> **Note:** The first run downloads and builds everything from scratch, which may take several minutes. Subsequent runs are cached in `/nix/store/` and are essentially instant. + +--- + +## File Structure + +| File | Purpose | +|------|---------| +| `flake.nix` | Main entry point — imports from `nix/`, defines packages and devShell | +| `flake.lock` | Pins nixpkgs and flake-utils to exact revisions | +| `nix/package.nix` | Rust package derivation: source filtering, native deps (OpenBLAS, protobuf), build flags | +| `nix/shell.nix` | Development shell: inherits build deps + adds Rust tools, Python/maturin, debuggers | +| `nix/banner.nix` | ASCII art banner displayed on `nix develop` | +| `nix/container.nix` | OCI container images for larql-server and larql CLI (Linux only) | +| `nix/models.nix` | Model catalog: HuggingFace models fetched as fixed-output derivations via git-lfs | +| `nix/demo.nix` | Demo apps: per-model vindex extraction and `nix run` targets | +| `nix/patches/use-system-protoc.patch` | Build patch: uses nixpkgs protoc instead of bundled protobuf-src | +| `nix/README.md` | This documentation | + +### Adding New Modules + +To add a new Nix module (e.g., NixOS module, test runner): + +1. Create `nix/your-module.nix` following the `{ pkgs, lib, ... }: ...` pattern +2. Import it in `flake.nix` with `import (nixDir + "/your-module.nix") { inherit pkgs lib; }` +3. Wire it into the appropriate output (`packages`, `apps`, `checks`, etc.) + +--- + +## Common Commands + +| Command | Description | +|---------|-------------| +| `nix develop` | Enter development shell with all tools | +| `nix build` | Build the larql package (output in `./result/`) | +| `nix flake show` | List all flake outputs | +| `nix flake check` | Validate flake structure | +| `cargo build --release` | Build optimised binary (inside dev shell) | +| `cargo test` | Run all workspace tests | +| `make ci` | fmt-check + clippy + test | +| `make fmt` | Format all code | +| `make lint` | Run clippy with `-D warnings` | +| `nix build .#container` | Build larql-server OCI image (Linux only) | +| `nix build .#container-cli` | Build larql CLI OCI image (Linux only) | +| `nix run .#demo-list` | List available demo models | +| `nix run .#demo` | Walk default model (gemma4-4b) | +| `nix run .#demo-` | Walk a specific model | +| `nix run .#demo-info` | Show GGUF metadata for local models | +| `nix build .#model-` | Fetch a model from HuggingFace | +| `nix build .#vindex-` | Build a vindex from a model | + +--- + +## Demo Models + +The flake includes a catalog of HuggingFace models that are fetched as Nix derivations (via `git-lfs`), with vindexes extracted at build time. Everything is cached in `/nix/store` — no runtime downloads needed. + +### Available Models + +| Key | Model | Size | +|-----|-------|------| +| `smollm2-360m` | SmolLM2 360M Instruct | ~725MB | +| `qwen-05b` | Qwen2.5 0.5B Instruct | ~970MB | +| `gemma4-2b` | Gemma 4 E2B Instruct | ~2.0GB | +| `tinyllama-1b` | TinyLlama 1.1B Chat v1.0 | ~2.2GB | +| `qwen-15b` | Qwen2.5 1.5B Instruct | ~3.1GB | +| `stablelm-2b` | StableLM 2 Zephyr 1.6B | ~3.2GB | +| `gemma4-4b` | Gemma 4 E4B Instruct (default) | ~4.0GB | +| `phi-35-mini` | Phi-3.5 Mini Instruct (3.8B) | ~7.6GB | + +### Running a Demo + +```bash +# List all available models +nix run .#demo-list + +# Run default model (gemma4-4b) with default prompt +nix run .#demo + +# Run a specific model +nix run .#demo-tinyllama-1b + +# Run with a custom prompt +nix run .#demo-qwen-15b -- "The meaning of life is" + +# Build just the vindex (without running) +nix build .#vindex-smollm2-360m +``` + +On first run, Nix fetches the model from HuggingFace and builds the vindex. Subsequent runs are instant (cached in `/nix/store`). + +### Adding a Model + +1. Prefetch the model hash: + ```bash + nix run nixpkgs#nix-prefetch-git -- --fetch-lfs https://huggingface.co// + ``` +2. Copy the `rev` and `hash` into a new entry in `nix/models.nix` +3. The model automatically gets `model-`, `vindex-`, and `demo-` targets + +> **Note:** Gated models (Llama, Gemma) require HuggingFace authentication and won't work without `HF_TOKEN`. Prefer ungated models for the catalog. + +--- + +## OCI Containers + +Two container images are available (Linux only), built with `dockerTools.buildLayeredImage` for optimal layer caching. + +### larql-server + +```bash +# Build and load +nix build .#container +docker load < result + +# Run with a vindex directory +docker run -d -p 8080:8080 \ + -v /path/to/vindexes:/data \ + larql-server:latest /data/my.vindex + +# With gRPC enabled +docker run -d -p 8080:8080 -p 50051:50051 \ + -v /path/to/vindexes:/data \ + larql-server:latest /data/my.vindex --grpc-port 50051 + +# With CORS and API key +docker run -d -p 8080:8080 \ + -v /path/to/vindexes:/data \ + larql-server:latest /data/my.vindex --cors --api-key mysecret +``` + +### larql CLI + +```bash +# Build and load +nix build .#container-cli +docker load < result + +# Run a command +docker run --rm -v /path/to/vindexes:/data larql:latest repl /data/my.vindex +``` + +Both containers: +- Run as non-root user `larql` (UID 1000) +- Include TLS certificates for HuggingFace downloads (`hf://` paths) +- Mount vindex data at `/data` + +--- + +## Python Bindings + +The `larql-python` crate (PyO3 bindings) is excluded from the Nix package build because it requires `maturin`. Instead, build it inside the dev shell: + +```bash +nix develop +cd crates/larql-python +maturin develop --release +pytest tests/ -v +``` + +--- + +## Notes + +- **`Cargo.lock` is committed** — `rustPlatform.buildRustPackage` requires a lock file for reproducible builds. This is best practice for application repositories (per Cargo documentation). +- **Cross-platform** — The flake supports both Linux (OpenBLAS) and macOS (Accelerate framework). Metal GPU support is available on Apple Silicon via `--features metal` in the dev shell. +- **`result` symlinks** — `nix build` creates a `result` symlink in the repo root. These are gitignored. + +--- + +## Troubleshooting + +**Issue: `nix develop` fails with "experimental features" error** +```bash +# Solution: Enable flakes permanently +echo 'experimental-features = nix-command flakes' | sudo tee -a /etc/nix/nix.conf +``` + +**Issue: Build fails with missing `Cargo.lock`** +```bash +# Solution: Generate the lock file +cargo generate-lockfile +``` + +**Issue: OpenBLAS not found during build** +```bash +# This should be handled automatically by the Nix derivation. +# If building outside Nix, install openblas-dev (or equivalent) for your distro. +``` + +**Issue: protobuf version mismatch** +```bash +# The Nix derivation sets PROTOC to use the Nix-provided protobuf. +# Inside nix develop, protoc is available on PATH automatically. +``` diff --git a/nix/banner.nix b/nix/banner.nix new file mode 100644 index 000000000..f7c98b733 --- /dev/null +++ b/nix/banner.nix @@ -0,0 +1,28 @@ +# +# nix/banner.nix - LARQL development shell banner +# +# ASCII art and welcome message displayed on `nix develop`. +# +'' + echo "" + echo " ╦ ╔═╗ ╦═╗ ╔═╗ ╦" + echo " ║ ╠═╣ ╠╦╝ ║═╬╗║" + echo " ╩═╝ ╩ ╩ ╩╚═ ╚═╝╚╩═╝" + echo " Lazarus Query Language v0.1" + echo "" + echo " Query engine for transformer model weights" + echo " The model is the database." + echo "" + echo " cargo build --release Build optimised binary" + echo " cargo test Run all workspace tests" + echo " make ci fmt-check + clippy + test" + echo " nix build Build via Nix" + echo "" + echo " Demo (models fetched + vindexes built by Nix):" + echo " nix run .#demo-list List available models" + echo " nix run .#demo Walk default model (gemma4-4b)" + echo ' nix run .#demo- Walk a specific model' + echo " nix run .#repl Interactive REPL (gemma4-4b)" + echo ' nix run .#repl- REPL with a specific model' + echo "" +'' diff --git a/nix/container.nix b/nix/container.nix new file mode 100644 index 000000000..3b5a3f085 --- /dev/null +++ b/nix/container.nix @@ -0,0 +1,131 @@ +# +# nix/container.nix - LARQL OCI container images +# +# Uses buildLayeredImage for better Docker layer caching. +# Container runs as non-root 'larql' user (UID 1000). +# +# Build: +# nix build .#container # larql-server image +# nix build .#container-cli # larql CLI image +# +# Load & Run: +# docker load < result +# docker run -d -p 8080:8080 -v /path/to/vindexes:/data larql-server:latest /data/my.vindex +# +# With gRPC: +# docker run -d -p 8080:8080 -p 50051:50051 \ +# -v /path/to/vindexes:/data larql-server:latest \ +# /data/my.vindex --grpc-port 50051 +# +{ pkgs, lib, larql }: +let + user = { + name = "larql"; + uid = 1000; + gid = 1000; + }; + + # NSS files for user/group resolution (required for non-root containers) + passwd = pkgs.writeTextDir "etc/passwd" '' + root:x:0:0:root:/root:/bin/sh + ${user.name}:x:${toString user.uid}:${toString user.gid}:larql:/home/${user.name}:/bin/sh + ''; + + group = pkgs.writeTextDir "etc/group" '' + root:x:0: + ${user.name}:x:${toString user.gid}: + ''; + + # Shared base contents for both images + baseContents = [ + pkgs.bashInteractive + pkgs.coreutils + pkgs.cacert # TLS certificates for HuggingFace downloads + passwd + group + ]; + + # ─── larql-server Container ────────────────────────────────────���───────── + server = pkgs.dockerTools.buildLayeredImage { + name = "larql-server"; + tag = "latest"; + + contents = baseContents ++ [ larql ]; + + extraCommands = '' + mkdir -p home/${user.name} + mkdir -p data + mkdir -p tmp + chmod 1777 tmp + ''; + + fakeRootCommands = '' + chown -R ${toString user.uid}:${toString user.gid} home/${user.name} + chown -R ${toString user.uid}:${toString user.gid} data + ''; + + config = { + User = user.name; + Entrypoint = [ "${larql}/bin/larql-server" ]; + Cmd = [ "--help" ]; + ExposedPorts = { + "8080/tcp" = {}; # HTTP (default) + "50051/tcp" = {}; # gRPC (optional, enabled via --grpc-port) + }; + WorkingDir = "/home/${user.name}"; + Volumes = { + "/data" = {}; + }; + Env = [ + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + ]; + Labels = { + "org.opencontainers.image.source" = "https://github.com/chrishayuk/chuk-larql-rs"; + "org.opencontainers.image.description" = "LARQL server — query engine for transformer model weights"; + "org.opencontainers.image.licenses" = "Apache-2.0"; + }; + }; + }; + + # ─── larql CLI Container ───────────────────────────────────────────────── + cli = pkgs.dockerTools.buildLayeredImage { + name = "larql"; + tag = "latest"; + + contents = baseContents ++ [ larql ]; + + extraCommands = '' + mkdir -p home/${user.name} + mkdir -p data + mkdir -p tmp + chmod 1777 tmp + ''; + + fakeRootCommands = '' + chown -R ${toString user.uid}:${toString user.gid} home/${user.name} + chown -R ${toString user.uid}:${toString user.gid} data + ''; + + config = { + User = user.name; + Entrypoint = [ "${larql}/bin/larql" ]; + Cmd = [ "--help" ]; + WorkingDir = "/home/${user.name}"; + Volumes = { + "/data" = {}; + }; + Env = [ + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + ]; + Labels = { + "org.opencontainers.image.source" = "https://github.com/chrishayuk/chuk-larql-rs"; + "org.opencontainers.image.description" = "LARQL CLI — query engine for transformer model weights"; + "org.opencontainers.image.licenses" = "Apache-2.0"; + }; + }; + }; + +in +{ + inherit server cli; +} diff --git a/nix/demo.nix b/nix/demo.nix new file mode 100644 index 000000000..79d2b0005 --- /dev/null +++ b/nix/demo.nix @@ -0,0 +1,168 @@ +# +# nix/demo.nix - LARQL demo apps +# +# Fetches models from HuggingFace as Nix derivations (git-lfs), +# extracts vindexes at build time, and provides `nix run` apps to query them. +# +# Everything is cached in /nix/store — no runtime downloads needed. +# +# Apps: +# nix run .#demo-list List available models +# nix run .#demo Walk default model (gemma4-4b) +# nix run .#demo- Walk a specific model +# nix run .#repl- Interactive REPL with model pre-loaded +# nix run .#demo-info Show GGUF metadata for local models +# +# Packages: +# nix build .#model- Fetch a model from HuggingFace +# nix build .#vindex- Extract a vindex from a model +# +{ pkgs, lib, larql }: +let + models = import ./models.nix { inherit pkgs; }; + + # Default model for `nix run .#demo` + defaultModel = "gemma4-4b"; + defaultPrompt = "The capital of France is"; + + # Build a vindex derivation from a model + mkVindex = name: model: pkgs.runCommand "larql-vindex-${name}" { + nativeBuildInputs = [ larql ]; + } '' + mkdir -p $out + larql extract-index ${model.src} -o $out + ''; + + mkApp = name: script: { + type = "app"; + program = "${pkgs.writeShellScript name script}"; + }; + + # Generate per-model packages and apps + modelNames = builtins.attrNames models; + + vindexes = lib.mapAttrs mkVindex models; + + modelPackages = lib.foldl' (acc: name: acc // { + "model-${name}" = models.${name}.src; + "vindex-${name}" = vindexes.${name}; + }) {} modelNames; + + modelApps = lib.foldl' (acc: name: + let + model = models.${name}; + vindex = vindexes.${name}; + in acc // { + "demo-${name}" = mkApp "larql-demo-${name}" '' + set -euo pipefail + PROMPT="''${1:-${defaultPrompt}}" + + echo "=== LARQL Demo: ${model.name} ===" + echo "Vindex: ${vindex}" + echo "Prompt: $PROMPT" + echo "" + + ${larql}/bin/larql walk --index ${vindex} -p "$PROMPT" -k 10 + ''; + + "repl-${name}" = mkApp "larql-repl-${name}" '' + echo "=== LARQL REPL: ${model.name} ===" + echo "" + exec ${pkgs.expect}/bin/expect -c ' + spawn ${larql}/bin/larql repl + expect "larql>" + send "USE \"${vindex}\";\r" + interact + ' + ''; + } + ) {} modelNames; + + # Model listing (sorted by size for display) + modelList = lib.concatStringsSep "\n" (map (name: + let m = models.${name}; + in " ${name}|${m.name}|${m.size}" + ) modelNames); + +in +{ + packages = modelPackages; + + apps = modelApps // { + # Default demo (uses default model) + demo = mkApp "larql-demo" '' + set -euo pipefail + PROMPT="''${1:-${defaultPrompt}}" + + echo "=== LARQL Demo: ${models.${defaultModel}.name} ===" + echo "Vindex: ${vindexes.${defaultModel}}" + echo "Prompt: $PROMPT" + echo "" + + ${larql}/bin/larql walk --index ${vindexes.${defaultModel}} -p "$PROMPT" -k 10 + ''; + + # Default REPL (uses default model) + repl = mkApp "larql-repl" '' + echo "=== LARQL REPL: ${models.${defaultModel}.name} ===" + echo "" + exec ${pkgs.expect}/bin/expect -c ' + spawn ${larql}/bin/larql repl + expect "larql>" + send "USE \"${vindexes.${defaultModel}}\";\r" + interact + ' + ''; + + # List available models + demo-list = mkApp "larql-demo-list" '' + echo "Available LARQL demo models:" + echo "" + printf " %-20s %-35s %s\n" "KEY" "MODEL" "SIZE" + printf " %-20s %-35s %s\n" "---" "-----" "----" + ${lib.concatStringsSep "\n" (map (name: + let m = models.${name}; + in ''printf " %-20s %-35s %s\n" "${name}" "${m.name}" "${m.size}"'' + ) modelNames)} + echo "" + echo "Run a model:" + echo " nix run .#demo # default (${defaultModel})" + echo " nix run .#demo- # specific model" + echo " nix run .#demo- -- \"your prompt\" # custom prompt" + echo "" + echo "Interactive REPL:" + echo " nix run .#repl # default (${defaultModel})" + echo " nix run .#repl- # specific model" + echo "" + echo "Build a vindex:" + echo " nix build .#vindex-" + ''; + + # Show GGUF file metadata (works with local llama.cpp cache) + demo-info = mkApp "larql-demo-info" '' + set -euo pipefail + GGUF_DIR="$HOME/.cache/llama.cpp" + + if [ -n "''${1:-}" ]; then + ${larql}/bin/larql convert gguf-info "$1" + exit 0 + fi + + if [ ! -d "$GGUF_DIR" ]; then + echo "No GGUF files found in $GGUF_DIR" + echo "Usage: nix run .#demo-info -- /path/to/model.gguf" + exit 1 + fi + + echo "Available GGUF models in $GGUF_DIR:" + echo "" + for f in "$GGUF_DIR"/*.gguf; do + size=$(du -h "$f" | cut -f1) + name=$(basename "$f") + echo " $name ($size)" + done + echo "" + echo "Run with: nix run .#demo-info -- " + ''; + }; +} diff --git a/nix/example-queries.md b/nix/example-queries.md new file mode 100644 index 000000000..8e0cf4884 --- /dev/null +++ b/nix/example-queries.md @@ -0,0 +1,42 @@ +Example queries from the video: + +LLMs Are Databases - So Query Them +https://youtu.be/8Ppw8254nLI?si=80kLyWlgvn-CRp35 + +STATS; + +DESCRIBE "France"; + +SELECT * FROM EDGE WHERE entity = "France" AND relation = "nationality" LIMIT 5; + +DESCRIBE "Einstein"; + +SELECT * FROM EDGES NEAREST TO "France" AT LAYER 26 LIMIT 10; + +SELECT * FROM FEATURES WHERE LAYER = 26; + +SELECT * FROM FEATURES WHERE LAYER = 25 AND feature = 5067; + +SHOW RELATIONS; + +SELECT * FROM EDGES WHERE relation = "capital" LIMIT 100; + +SELECT * FROM EDGES NEAREST TO "Einstein" AT LAYER 26 LIMIT 10; + +SELECT * FROM ENTITIES LIMIT 20; + +SELECT * FROM FEATURES WHERE LAYER = 26 LIMIT 20; + +INFER "The captial of France is" TOP 5; + +INFER "The captial of Atlantis is" TOP 5; + +INSERT INTO EDGES (entity,relation,target) VALUES ("Atlantis","capital","Poseidon"); + +INFER "The captial of Atlantis is" TOP 5; + +DESCRIBE "Atlantis"; + +COMPILE CURRENT INTO VINDEX "/tmp/atlantis.vindex"; + + diff --git a/nix/models.nix b/nix/models.nix new file mode 100644 index 000000000..745bfb96d --- /dev/null +++ b/nix/models.nix @@ -0,0 +1,100 @@ +# +# nix/models.nix - Model catalog for LARQL demos +# +# Each model is fetched from HuggingFace via git-lfs as a fixed-output +# derivation, fully reproducible and cached in /nix/store. +# +# To add a model: +# 1. Run: nix run nixpkgs#nix-prefetch-git -- --fetch-lfs https://huggingface.co// +# 2. Copy the rev and hash into a new entry below +# 3. Gated models (Llama, Gemma 3) require HF authentication — Gemma 4 is ungated +# +# List available models: nix run .#demo-list +# Run a specific model: nix run .#demo -- --model qwen-0.5b "your prompt" +# +{ pkgs }: +let + fetchModel = { name, url, rev, hash, size ? "unknown" }: + { + inherit name size; + src = pkgs.fetchgit { + inherit url rev hash; + fetchLFS = true; + }; + }; +in +{ + # ─── Tiny Models (< 1GB safetensors) ───────────────────────────────── + "smollm2-360m" = fetchModel { + name = "SmolLM2 360M Instruct"; + url = "https://huggingface.co/HuggingFaceTB/SmolLM2-360M-Instruct"; + rev = "a10cc1512eabd3dde888204e902eca88bddb4951"; + hash = "sha256-POlS7POP/lLqTdnL3CoSLOtPqACPC2D1BVgyq3kfWAo="; + size = "~725MB"; + }; + + qwen-05b = fetchModel { + name = "Qwen2.5 0.5B Instruct"; + url = "https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct"; + rev = "7ae557604adf67be50417f59c2c2f167def9a775"; + hash = "sha256-Sg7nluvDsQhs0uBgcFtAwA5QErNC8tJFUkg6hiU353E="; + size = "~970MB"; + }; + + # ─── Small Models (1-3GB safetensors) ──────────────────────────────── + gemma4-2b = fetchModel { + name = "Gemma 4 E2B Instruct"; + url = "https://huggingface.co/google/gemma-4-E2B-it"; + rev = "b4a601102c3d45e2b7b50e2057a6d5ec8ed4adcf"; + hash = "sha256-CAaf8jR7FbGoHkcQVN/aoJi7FBpv7bEY7gDZFnNCIJc="; + size = "~2.0GB"; + }; + + tinyllama-1b = fetchModel { + name = "TinyLlama 1.1B Chat v1.0"; + url = "https://huggingface.co/TinyLlama/TinyLlama-1.1B-Chat-v1.0"; + rev = "fe8a4ea1ffedaf415f4da2f062534de366a451e6"; + hash = "sha256-vp/aUHKX+NJZZMIk2CgSh2czeGD0HeQGS30p/If2pA0="; + size = "~2.2GB"; + }; + + qwen-15b = fetchModel { + name = "Qwen2.5 1.5B Instruct"; + url = "https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct"; + rev = "989aa7980e4cf806f80c7fef2b1adb7bc71aa306"; + hash = "sha256-YnR8BMkrPBXHs8RFbGVKiOG/Y2etyqOBlYm5t+kcNk8="; + size = "~3.1GB"; + }; + + stablelm-2b = fetchModel { + name = "StableLM 2 Zephyr 1.6B"; + url = "https://huggingface.co/stabilityai/stablelm-2-zephyr-1_6b"; + rev = "2f275b1127d59fc31e4f7c7426d528768ada9ea4"; + hash = "sha256-qSsq2ZK5JqaA5VEFgbUgFgcb98bS4/7CWzB/COMjNro="; + size = "~3.2GB"; + }; + + # ─── Medium Models (3-8GB safetensors) ─────────────────────────────── + gemma4-4b = fetchModel { + name = "Gemma 4 E4B Instruct"; + url = "https://huggingface.co/google/gemma-4-E4B-it"; + rev = "83df0a889143b1dbfc61b591bbc639540fd9ce4c"; + hash = "sha256-fEorwLDWBpJaN1QSBVHI6gtdmM48tKegSzMN7eWyjm4="; + size = "~4.0GB"; + }; + + phi-35-mini = fetchModel { + name = "Phi-3.5 Mini Instruct (3.8B)"; + url = "https://huggingface.co/microsoft/Phi-3.5-mini-instruct"; + rev = "2fe192450127e6a83f7441aef6e3ca586c338b77"; + hash = "sha256-HnreQJ2iSEIFef5UxKrgYfbI3I0xlox38C3J6LJPgSE="; + size = "~7.6GB"; + }; + + # ─── Gated Models (require HF authentication) ─────────────────────── + # These are listed for reference but won't work without HF_TOKEN. + # To add: accept license on HF website, set HF_TOKEN, then prefetch. + # + # gemma-3-1b = { ... }; # google/gemma-3-1b-it + # llama-3.2-1b = { ... }; # meta-llama/Llama-3.2-1B-Instruct +} diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 000000000..8bf8e883e --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,90 @@ +# +# nix/package.nix - LARQL Rust package derivation +# +# Builds the larql workspace (excluding larql-python which requires maturin). +# Provides the larql-cli binary and all library crates. +# +# Patches: +# use-system-protoc.patch - Removes protobuf-src build dependency from +# larql-server so it uses nixpkgs protoc instead of compiling from source. +# +{ pkgs, lib, src }: +let + # Filter out files not needed for the build + srcFiltered = lib.cleanSourceWith { + inherit src; + filter = path: type: + let + baseName = builtins.baseNameOf path; + relPath = lib.removePrefix (toString src + "/") (toString path); + in + # Exclude nix packaging files, build artifacts, and editor config + !(lib.hasPrefix "nix/" relPath) + && !(lib.hasPrefix "flake" baseName) + && !(lib.hasPrefix "result" baseName) + && !(lib.hasPrefix "target/" relPath) + && !(lib.hasPrefix ".git/" relPath); + }; +in +pkgs.rustPlatform.buildRustPackage { + pname = "larql"; + version = "0.1.0"; + src = srcFiltered; + + cargoHash = "sha256-6mvESL1m5sZZCx8YdArgTkwNnGHRQz3RPub/hVYelqg="; + + # Use system protoc instead of bundled protobuf-src + cargoPatches = [ + ./patches/use-system-protoc.patch + ]; + + nativeBuildInputs = with pkgs; [ + pkg-config + protobuf # provides protoc for tonic-build + cmake # needed by protobuf-src build script (still in Cargo.lock as transitive dep) + ]; + + buildInputs = with pkgs; [ + openssl + ] ++ lib.optionals stdenv.hostPlatform.isLinux [ + openblas + ] ++ lib.optionals stdenv.hostPlatform.isDarwin (with darwin.apple_sdk.frameworks; [ + Accelerate + Security + SystemConfiguration + ]); + + # Point tonic-build to nixpkgs protoc + PROTOC = "${pkgs.protobuf}/bin/protoc"; + + # Point openblas-src to system library + OPENBLAS_LIB_DIR = lib.optionalString pkgs.stdenv.hostPlatform.isLinux + "${pkgs.openblas}/lib"; + + # Exclude larql-python (requires maturin, handled separately in dev shell) + # Patch sets larql-cli default = [] (removes metal); re-enable on Darwin + cargoBuildFlags = [ + "--workspace" + "--exclude" "larql-python" + ] ++ lib.optionals pkgs.stdenv.hostPlatform.isDarwin [ + "--features" "larql-cli/metal" + ]; + + cargoTestFlags = [ + "--workspace" + "--exclude" "larql-python" + ] ++ lib.optionals pkgs.stdenv.hostPlatform.isDarwin [ + "--features" "larql-cli/metal" + ]; + + # Skip tests — upstream has a pre-existing compile error in test_architectures + # (missing fields packed_byte_ranges and packed_mmaps in ModelWeights) + doCheck = false; + + meta = with lib; { + description = "Query engine for transformer model weights — the model is the database"; + homepage = "https://github.com/chrishayuk/chuk-larql-rs"; + license = licenses.asl20; + mainProgram = "larql"; + }; +} diff --git a/nix/patches/use-system-protoc.patch b/nix/patches/use-system-protoc.patch new file mode 100644 index 000000000..316c7a089 --- /dev/null +++ b/nix/patches/use-system-protoc.patch @@ -0,0 +1,71 @@ +--- a/crates/larql-server/Cargo.toml ++++ b/crates/larql-server/Cargo.toml +@@ -42,4 +42,3 @@ + + [build-dependencies] + tonic-build = "0.13" +-protobuf-src = "2" +--- a/crates/larql-server/build.rs ++++ b/crates/larql-server/build.rs +@@ -1,6 +1,5 @@ + fn main() -> Result<(), Box> { +- // Use bundled protoc (no system install required). +- std::env::set_var("PROTOC", protobuf_src::protoc()); ++ // Use system protoc (set via PROTOC env var by Nix). + tonic_build::compile_protos("proto/vindex.proto")?; + Ok(()) + } +--- a/crates/larql-router-protocol/Cargo.toml ++++ b/crates/larql-router-protocol/Cargo.toml +@@ -14,4 +14,3 @@ + + [build-dependencies] + tonic-build = "0.13" +-protobuf-src = "2" +--- a/crates/larql-router-protocol/build.rs ++++ b/crates/larql-router-protocol/build.rs +@@ -1,5 +1,4 @@ + fn main() -> Result<(), Box> { +- std::env::set_var("PROTOC", protobuf_src::protoc()); + tonic_build::compile_protos("proto/grid.proto")?; + Ok(()) + } +--- a/crates/larql-cli/Cargo.toml ++++ b/crates/larql-cli/Cargo.toml +@@ -32,7 +32,7 @@ + libc = "0.2" + + [features] +-default = ["metal"] ++default = [] + metal = [ + "larql-compute/metal", + "larql-inference/metal", +--- a/crates/larql-cli/src/commands/primary/bench_cmd.rs ++++ b/crates/larql-cli/src/commands/primary/bench_cmd.rs +@@ -156,12 +156,19 @@ + &tokenizer, &*weights.arch, args.prompt.as_str(), + ).map_err(|e| format!("tokenize: {e}"))?; + +- let backend: Box = if metal { +- let b = larql_compute::metal::MetalBackend::new() +- .ok_or("Metal backend unavailable — rebuild with `--features metal` on an M-series Mac")?; +- Box::new(b) +- } else { +- Box::new(larql_compute::CpuBackend) ++ let backend: Box = { ++ #[cfg(feature = "metal")] ++ { ++ if metal { ++ let b = larql_compute::metal::MetalBackend::new() ++ .ok_or("Metal backend unavailable — rebuild with `--features metal` on an M-series Mac")?; ++ Box::new(b) ++ } else { ++ Box::new(larql_compute::CpuBackend) ++ } ++ } ++ #[cfg(not(feature = "metal"))] ++ { Box::new(larql_compute::CpuBackend) } + }; + + let cached_layers = CachedLayerGraph::from_residuals(Vec::new()); diff --git a/nix/shell.nix b/nix/shell.nix new file mode 100644 index 000000000..475870cf5 --- /dev/null +++ b/nix/shell.nix @@ -0,0 +1,38 @@ +# +# nix/shell.nix - LARQL development shell +# +# Provides build dependencies plus Rust tooling, Python bindings support, +# and debugging tools. +# +{ pkgs, lib, larql }: +let + banner = import ./banner.nix; + + # Python with packages needed for larql-python development and testing + pythonEnv = pkgs.python3.withPackages (ps: with ps; [ + pytest + numpy + ]); +in +pkgs.mkShell { + inputsFrom = [ larql ]; + + packages = with pkgs; [ + # Rust development tools + rust-analyzer + clippy + rustfmt + cargo-watch + + # Python bindings (larql-python via maturin) + pythonEnv + maturin + ] ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ + gdb + valgrind + ] ++ lib.optionals pkgs.stdenv.hostPlatform.isDarwin [ + lldb + ]; + + shellHook = banner; +} From 505434d400b288f7dabd2d638b59393fce719fb0 Mon Sep 17 00:00:00 2001 From: Dmitry Dorofeev Date: Tue, 5 May 2026 18:58:28 +0300 Subject: [PATCH 2/3] fix(build): restore deleted extract/build.rs and align stale test/example initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Virtual Experts merge (PR #45) left `main` unable to build: 1. `crates/larql-vindex/src/extract/build.rs` was deleted but `extract/mod.rs` still declares `pub mod build;` and re-exports `build_vindex` / `build_vindex_resume`, which are still called from `larql-cli`, `larql-lql`, the vindex tests, and the `demo_features` example. The roadmap (vindex M8) plans to split this file into `build/{mod,...}.rs`, but that refactor was never finished. Restore the file from `d3a8bc6^` so the workspace compiles again; the planned split can land separately. 2. New required fields had been added to several public structs but a number of test fixtures and examples were not updated, so `cargo build --features metal` and `cargo test --workspace` failed with E0063 / E0308: - `FullPipelineLayer.ffn_is_remote` — added to 6 `larql-compute` examples (`compare_decode`, `compare_formats`, `compare_ollama`, `compare_pipeline`, `demo_architecture`, `diag_decode_pipeline`). - `LoadVindexOptions.moe_remote` — added to `bench_expert_server` and `openai_demo` examples. - `LoadedModel.{unit_filter, moe_remote}` plus the `metal-experts`-conditional `metal_backend` / `moe_scratches` / `metal_ffn_layer_bufs` fields, and the `weights` field's new `OnceLock>` wrapper — fixed in `tests/common/mod.rs`, `tests/test_expert_endpoint.rs`, `tests/test_http_full_routes.rs`, `tests/test_unit_band_utils.rs`, `tests/test_unit_state.rs`. - `VindexConfig.{fp4, ffn_layout}` and `ModelWeights.skipped_tensors` in `tests/test_expert_endpoint.rs`. - `MoeLayerWeights.expert_data_format` plus per-expert byte-slice layout (`Vec<&[u8]>` instead of a single `&[u8]`) in the same test's `local_output` helper. Result: workspace builds clean with `--features metal`, and 3,481 tests run (the remaining failures are runtime numerical-correctness issues in Metal kernels — unrelated to this commit, and the reason this branch exists). Co-Authored-By: Claude Opus 4.7 --- .../larql-compute/examples/compare_decode.rs | 2 + .../larql-compute/examples/compare_formats.rs | 3 + .../larql-compute/examples/compare_ollama.rs | 5 + .../examples/compare_pipeline.rs | 2 + .../examples/demo_architecture.rs | 1 + .../examples/diag_decode_pipeline.rs | 3 + .../examples/bench_expert_server.rs | 1 + crates/larql-server/examples/openai_demo.rs | 1 + crates/larql-server/tests/common/mod.rs | 21 + .../tests/test_expert_endpoint.rs | 21 +- .../tests/test_http_full_routes.rs | 7 + .../tests/test_unit_band_utils.rs | 7 + crates/larql-server/tests/test_unit_state.rs | 14 + crates/larql-vindex/src/extract/build.rs | 1113 +++++++++++++++++ 14 files changed, 1198 insertions(+), 3 deletions(-) create mode 100644 crates/larql-vindex/src/extract/build.rs diff --git a/crates/larql-compute/examples/compare_decode.rs b/crates/larql-compute/examples/compare_decode.rs index 5b084117a..5402e8b02 100644 --- a/crates/larql-compute/examples/compare_decode.rs +++ b/crates/larql-compute/examples/compare_decode.rs @@ -176,6 +176,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -282,6 +283,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) diff --git a/crates/larql-compute/examples/compare_formats.rs b/crates/larql-compute/examples/compare_formats.rs index 179fdd742..44ae3328b 100644 --- a/crates/larql-compute/examples/compare_formats.rs +++ b/crates/larql-compute/examples/compare_formats.rs @@ -190,6 +190,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -293,6 +294,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -396,6 +398,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) diff --git a/crates/larql-compute/examples/compare_ollama.rs b/crates/larql-compute/examples/compare_ollama.rs index f254da8fa..c02170ef1 100644 --- a/crates/larql-compute/examples/compare_ollama.rs +++ b/crates/larql-compute/examples/compare_ollama.rs @@ -194,6 +194,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -279,6 +280,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -365,6 +367,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -458,6 +461,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -541,6 +545,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) diff --git a/crates/larql-compute/examples/compare_pipeline.rs b/crates/larql-compute/examples/compare_pipeline.rs index b4a94e515..2a9443eba 100644 --- a/crates/larql-compute/examples/compare_pipeline.rs +++ b/crates/larql-compute/examples/compare_pipeline.rs @@ -192,6 +192,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) @@ -300,6 +301,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }) diff --git a/crates/larql-compute/examples/demo_architecture.rs b/crates/larql-compute/examples/demo_architecture.rs index 16b8fdad4..74135a068 100644 --- a/crates/larql-compute/examples/demo_architecture.rs +++ b/crates/larql-compute/examples/demo_architecture.rs @@ -184,6 +184,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }; diff --git a/crates/larql-compute/examples/diag_decode_pipeline.rs b/crates/larql-compute/examples/diag_decode_pipeline.rs index 82ef599a9..c4932ba46 100644 --- a/crates/larql-compute/examples/diag_decode_pipeline.rs +++ b/crates/larql-compute/examples/diag_decode_pipeline.rs @@ -132,6 +132,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }; @@ -299,6 +300,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }; @@ -388,6 +390,7 @@ fn main() { ffn_up_bias: None, ffn_down_bias: None, moe: None, + ffn_is_remote: false, moe_combined_output_norm: false, moe_outer_post_norm: None, }; diff --git a/crates/larql-server/examples/bench_expert_server.rs b/crates/larql-server/examples/bench_expert_server.rs index 137e72a47..afeffff11 100644 --- a/crates/larql-server/examples/bench_expert_server.rs +++ b/crates/larql-server/examples/bench_expert_server.rs @@ -269,6 +269,7 @@ fn main() { // first half — but we set this *after* peeking at num_experts below. expert_filter: None, unit_filter: None, + moe_remote: None, }; let path_str = args[1].clone(); diff --git a/crates/larql-server/examples/openai_demo.rs b/crates/larql-server/examples/openai_demo.rs index b588a69a0..ab5ecf7ee 100644 --- a/crates/larql-server/examples/openai_demo.rs +++ b/crates/larql-server/examples/openai_demo.rs @@ -139,6 +139,7 @@ fn load_default(path: &str) -> Result Arc { ffn_l2_cache: larql_server::ffn_l2_cache::FfnL2Cache::new(1), expert_filter: None, unit_filter: None, + moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(std::collections::HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), }) } @@ -171,6 +178,13 @@ pub fn model_infer_enabled(id: &str) -> Arc { ffn_l2_cache: larql_server::ffn_l2_cache::FfnL2Cache::new(1), expert_filter: None, unit_filter: None, + moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(std::collections::HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), }) } @@ -240,6 +254,13 @@ impl ModelBuilder { ffn_l2_cache: FfnL2Cache::new(1), expert_filter: None, unit_filter: None, + moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(std::collections::HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), }) } } diff --git a/crates/larql-server/tests/test_expert_endpoint.rs b/crates/larql-server/tests/test_expert_endpoint.rs index b3fe145e4..b814884b8 100644 --- a/crates/larql-server/tests/test_expert_endpoint.rs +++ b/crates/larql-server/tests/test_expert_endpoint.rs @@ -221,6 +221,8 @@ fn make_loaded_model( down_top_k: 1, has_model_weights: false, model_config: None, + fp4: None, + ffn_layout: None, }; // Build ModelWeights with expert data in raw_bytes (no mmap needed). @@ -238,6 +240,7 @@ fn make_loaded_model( vectors, raw_bytes, packed_mmaps: HashMap::new(), + skipped_tensors: Vec::new(), packed_byte_ranges: HashMap::new(), embed: embed.clone(), lm_head: embed, @@ -253,7 +256,7 @@ fn make_loaded_model( }; let lock = OnceLock::new(); - lock.set(weights).ok(); + lock.set(std::sync::RwLock::new(weights)).ok(); LoadedModel { id: "test-moe".into(), @@ -272,7 +275,14 @@ fn make_loaded_model( probe_labels: HashMap::new(), ffn_l2_cache: FfnL2Cache::new(1), expert_filter: None, + unit_filter: None, moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), } } @@ -307,11 +317,16 @@ fn local_output( router_proj: &[f32], pre_norm: &[f32], ) -> Vec { + let gate_up_per_expert = gate_up.len() / NUM_EXPERTS; + let down_per_expert = down.len() / NUM_EXPERTS; + let experts_gate_up: Vec<&[u8]> = gate_up.chunks(gate_up_per_expert).collect(); + let experts_down: Vec<&[u8]> = down.chunks(down_per_expert).collect(); cpu_moe_forward( h, &MoeLayerWeights { - experts_gate_up: gate_up, - experts_down: down, + experts_gate_up, + experts_down, + expert_data_format: larql_compute::QuantFormat::BF16, router_proj, router_scale: &[], router_per_expert_scale: &[], diff --git a/crates/larql-server/tests/test_http_full_routes.rs b/crates/larql-server/tests/test_http_full_routes.rs index 784e8a002..5ff99ae7c 100644 --- a/crates/larql-server/tests/test_http_full_routes.rs +++ b/crates/larql-server/tests/test_http_full_routes.rs @@ -48,6 +48,13 @@ fn model_functional_with_labels(id: &str) -> Arc { ffn_l2_cache: larql_server::ffn_l2_cache::FfnL2Cache::new(1), expert_filter: None, unit_filter: None, + moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(std::collections::HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), }) } diff --git a/crates/larql-server/tests/test_unit_band_utils.rs b/crates/larql-server/tests/test_unit_band_utils.rs index 187ce1bb2..38e060537 100644 --- a/crates/larql-server/tests/test_unit_band_utils.rs +++ b/crates/larql-server/tests/test_unit_band_utils.rs @@ -166,6 +166,13 @@ fn make_minimal_model(layer_bands: Option) -> Arc { ffn_l2_cache: FfnL2Cache::new(1), expert_filter: None, unit_filter: None, + moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(std::collections::HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), }) } diff --git a/crates/larql-server/tests/test_unit_state.rs b/crates/larql-server/tests/test_unit_state.rs index 06aef83e1..f37ca6258 100644 --- a/crates/larql-server/tests/test_unit_state.rs +++ b/crates/larql-server/tests/test_unit_state.rs @@ -96,6 +96,13 @@ fn make_tiny_model(id: &str) -> Arc { ffn_l2_cache: FfnL2Cache::new(1), expert_filter: None, unit_filter: None, + moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(std::collections::HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), }) } @@ -172,6 +179,13 @@ fn make_loaded_model_for_warmup() -> Arc { ffn_l2_cache: FfnL2Cache::new(1), expert_filter: None, unit_filter: None, + moe_remote: None, + #[cfg(feature = "metal-experts")] + metal_backend: std::sync::OnceLock::new(), + #[cfg(feature = "metal-experts")] + moe_scratches: std::sync::Mutex::new(std::collections::HashMap::new()), + #[cfg(feature = "metal-experts")] + metal_ffn_layer_bufs: std::sync::OnceLock::new(), }) } diff --git a/crates/larql-vindex/src/extract/build.rs b/crates/larql-vindex/src/extract/build.rs new file mode 100644 index 000000000..15c38a10f --- /dev/null +++ b/crates/larql-vindex/src/extract/build.rs @@ -0,0 +1,1113 @@ +//! Build a .vindex from model weights — the extraction/clustering pipeline. +//! +//! Two entry points: `build_vindex` (full pipeline from weights) and +//! `build_vindex_resume` (skip the heavy stages, rebuild clustering + +//! tokenizer + index.json from existing partial output). +//! +//! `build_vindex` is structured around a `BuildContext` that holds the +//! shared inputs + accumulator state across the stages: +//! 1. `write_gate_vectors` — gate matrices per layer (handles MoE) +//! 2. `write_embeddings` — embedding table +//! 3. `write_down_meta_and_clusters` — per-feature top-k tokens + collect +//! offset directions for clustering +//! 4. `run_clustering` — k-means + label clusters +//! 5. `write_tokenizer` +//! 6. `write_index_json` — config + provenance + checksums +//! +//! Discrete helpers live in `super::build_helpers`. + +use crate::extract::stage_labels::*; +use std::io::BufWriter; +use std::path::Path; + +use larql_models::{ModelWeights, TopKEntry, WeightArray}; + +use crate::config::dtype::{write_floats, StorageDtype}; +use crate::config::{VindexConfig, VindexLayerInfo, VindexModelConfig}; +use crate::error::VindexError; +use crate::format::filenames::*; + +use super::build_helpers::{ + build_whole_word_vocab, chrono_now, compute_gate_top_tokens, compute_offset_direction, + run_clustering_pipeline, ClusterData, +}; + +pub use crate::extract::callbacks::IndexBuildCallbacks; + +// ═══════════════════════════════════════════════════════════════════════ +// BuildContext — shared state across pipeline stages +// ═══════════════════════════════════════════════════════════════════════ + +/// Holds the inputs + accumulators for the build pipeline. Each stage +/// method on `BuildContext` reads inputs and mutates the accumulators +/// (`layer_infos`, `cluster_*`); the derived constants are set in `new`. +struct BuildContext<'a> { + // Inputs + weights: &'a ModelWeights, + tokenizer: &'a tokenizers::Tokenizer, + output_dir: &'a Path, + callbacks: &'a mut dyn IndexBuildCallbacks, + dtype: StorageDtype, + down_top_k: usize, + + // Derived constants + num_layers: usize, + hidden_size: usize, + intermediate_size: usize, + vocab_size: usize, + embed_scale: f32, + is_moe: bool, + n_experts: usize, + + // Stage 1 → Stage 6 (consumed by `write_index_json`) + layer_infos: Vec, + + // Stage 3 collects → Stage 4 drains (`run_clustering`). + cluster_directions: Vec, + cluster_features: Vec<(usize, usize)>, + cluster_top_tokens: Vec, + cluster_input_tokens: Vec, + cluster_output_tokens: Vec, +} + +impl<'a> BuildContext<'a> { + fn new( + weights: &'a ModelWeights, + tokenizer: &'a tokenizers::Tokenizer, + output_dir: &'a Path, + callbacks: &'a mut dyn IndexBuildCallbacks, + dtype: StorageDtype, + down_top_k: usize, + ) -> Self { + Self { + num_layers: weights.num_layers, + hidden_size: weights.hidden_size, + intermediate_size: weights.intermediate_size, + vocab_size: weights.vocab_size, + embed_scale: weights.arch.embed_scale(), + is_moe: weights.arch.is_moe(), + n_experts: weights.arch.num_experts(), + weights, + tokenizer, + output_dir, + callbacks, + dtype, + down_top_k, + layer_infos: Vec::new(), + cluster_directions: Vec::new(), + cluster_features: Vec::new(), + cluster_top_tokens: Vec::new(), + cluster_input_tokens: Vec::new(), + cluster_output_tokens: Vec::new(), + } + } + + /// Stage 1 — write `gate_vectors.bin` (one matrix per layer; MoE + /// concatenates each expert's matrix). Populates `layer_infos`. + fn write_gate_vectors(&mut self) -> Result<(), VindexError> { + self.callbacks.on_stage(STAGE_GATE_VECTORS); + let gate_path = self.output_dir.join(GATE_VECTORS_BIN); + let mut gate_file = BufWriter::new(std::fs::File::create(&gate_path)?); + let mut offset: u64 = 0; + + for layer in 0..self.num_layers { + self.callbacks + .on_layer_start(COMP_GATE, layer, self.num_layers); + let start = std::time::Instant::now(); + + if self.is_moe && self.n_experts > 0 { + // MoE: write each expert's gate matrix contiguously + let mut total_features = 0usize; + let mut layer_bytes = 0u64; + let mut features_per_expert = 0usize; + + for expert in 0..self.n_experts { + let gate_key = match self.weights.arch.expert_ffn_gate_key(layer, expert) { + Some(k) => k, + None => continue, + }; + let w_gate = match self.weights.tensors.get(&gate_key) { + Some(w) => w, + None => continue, + }; + features_per_expert = w_gate.shape()[0]; + total_features += features_per_expert; + let data = w_gate.as_slice().unwrap(); + layer_bytes += write_floats(&mut gate_file, data, self.dtype)?; + } + + // Also include shared expert if present + if let Some(shared_key) = self.weights.arch.shared_expert_gate_key(layer) { + if let Some(w_gate) = self.weights.tensors.get(&shared_key) { + let n = w_gate.shape()[0]; + total_features += n; + let data = w_gate.as_slice().unwrap(); + layer_bytes += write_floats(&mut gate_file, data, self.dtype)?; + } + } + + if total_features > 0 { + self.layer_infos.push(VindexLayerInfo { + layer, + num_features: total_features, + offset, + length: layer_bytes, + num_experts: Some(self.n_experts), + num_features_per_expert: Some(features_per_expert), + }); + offset += layer_bytes; + } + } else { + // Dense: single gate matrix per layer + let gate_key = self.weights.arch.ffn_gate_key(layer); + let w_gate = match self.weights.tensors.get(&gate_key) { + Some(w) => w, + None => continue, + }; + let num_features = w_gate.shape()[0]; + let data = w_gate.as_slice().unwrap(); + let length = write_floats(&mut gate_file, data, self.dtype)?; + self.layer_infos.push(VindexLayerInfo { + layer, + num_features, + offset, + length, + num_experts: None, + num_features_per_expert: None, + }); + offset += length; + } + + self.callbacks + .on_layer_done(COMP_GATE, layer, start.elapsed().as_secs_f64() * 1000.0); + } + self.callbacks.on_stage_done(STAGE_GATE_VECTORS, 0.0); + Ok(()) + } + + /// Stage 2 — write `embeddings.bin`. + fn write_embeddings(&mut self) -> Result<(), VindexError> { + self.callbacks.on_stage(STAGE_EMBEDDINGS); + let embed_path = self.output_dir.join(EMBEDDINGS_BIN); + let embed_data = self.weights.embed.as_slice().unwrap(); + let embed_bytes = crate::config::dtype::encode_floats(embed_data, self.dtype); + std::fs::write(&embed_path, &embed_bytes)?; + self.callbacks.on_stage_done(STAGE_EMBEDDINGS, 0.0); + Ok(()) + } + + /// Stage 3 — per-layer down-projection metadata + cluster collection. + /// + /// For each layer, project `embed @ w_down` to get vocab logits per + /// feature, take top-k as `FeatureMeta`. Knowledge layers (L14–28) + /// also collect `(input_token, output_token, offset_direction)` for + /// the relation clustering stage. + fn write_down_meta_and_clusters(&mut self) -> Result<(), VindexError> { + self.callbacks.on_stage(STAGE_DOWN_META); + + let mut all_down_meta: Vec>>> = + vec![None; self.num_layers]; + + let cluster_layer_min = 14.min(self.num_layers); + let cluster_layer_max = 28.min(self.num_layers); + + // Build whole-word vocab once, shared across layers + let (ww_ids_shared, ww_embed_shared) = build_whole_word_vocab( + self.tokenizer, + &self.weights.embed, + self.vocab_size, + self.hidden_size, + ); + + for (layer, layer_down_meta) in all_down_meta.iter_mut().enumerate().take(self.num_layers) { + self.callbacks + .on_layer_start(COMP_DOWN, layer, self.num_layers); + let start = std::time::Instant::now(); + + // Collect all down matrices for this layer (dense: 1, MoE: num_experts) + let down_matrices: Vec<(&WeightArray, usize)> = if self.is_moe && self.n_experts > 0 { + let mut mats = Vec::new(); + for expert in 0..self.n_experts { + if let Some(key) = self.weights.arch.expert_ffn_down_key(layer, expert) { + if let Some(w) = self.weights.tensors.get(&key) { + mats.push((w, expert)); + } + } + } + if let Some(key) = self.weights.arch.shared_expert_down_key(layer) { + if let Some(w) = self.weights.tensors.get(&key) { + mats.push((w, self.n_experts)); + } + } + mats + } else { + let down_key = self.weights.arch.ffn_down_key(layer); + match self.weights.tensors.get(&down_key) { + Some(w) => vec![(w, 0)], + None => { + self.callbacks.on_layer_done(COMP_DOWN, layer, 0.0); + continue; + } + } + }; + + if down_matrices.is_empty() { + self.callbacks.on_layer_done(COMP_DOWN, layer, 0.0); + continue; + } + + let total_features_this_layer: usize = + down_matrices.iter().map(|(w, _)| w.shape()[1]).sum(); + let is_knowledge_layer = layer >= cluster_layer_min && layer < cluster_layer_max; + + // Dense models: pre-compute gate top tokens for clustering. + // (MoE: skip — too many features.) + let gate_top_tokens: Vec = if is_knowledge_layer && !self.is_moe { + let num_features = down_matrices[0].0.shape()[1]; + compute_gate_top_tokens( + self.weights, + self.tokenizer, + layer, + num_features, + &ww_ids_shared, + &ww_embed_shared, + ) + } else { + vec![] + }; + + let mut feature_offset = 0usize; + for (w_down, _expert_id) in &down_matrices { + let num_features = w_down.shape()[1]; + let batch_size = 1024; + + for batch_start in (0..num_features).step_by(batch_size) { + let batch_end = (batch_start + batch_size).min(num_features); + self.callbacks.on_feature_progress( + "down", + layer, + feature_offset + batch_start, + total_features_this_layer, + ); + + let w_chunk = w_down + .slice(ndarray::s![.., batch_start..batch_end]) + .to_owned(); + let cpu = larql_compute::CpuBackend; + use larql_compute::MatMul; + let chunk_logits = cpu.matmul(self.weights.embed.view(), w_chunk.view()); + + for feat in batch_start..batch_end { + let col = chunk_logits.column(feat - batch_start); + let mut scores: Vec<(usize, f32)> = + col.iter().copied().enumerate().collect(); + + let k = self.down_top_k.min(scores.len()); + if k > 0 && k < scores.len() { + scores.select_nth_unstable_by(k, |a, b| { + b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal) + }); + } + scores.truncate(k); + scores.sort_unstable_by(|a, b| { + b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal) + }); + + let top_k_entries: Vec = scores + .into_iter() + .filter_map(|(idx, logit)| { + self.tokenizer + .decode(&[idx as u32], true) + .ok() + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .map(|token| TopKEntry { + token, + token_id: idx as u32, + logit, + }) + }) + .collect(); + + let (top_token, top_token_id, c_score) = + if let Some(first) = top_k_entries.first() { + (first.token.clone(), first.token_id, first.logit) + } else { + (String::new(), 0, 0.0) + }; + + // Collect gate→down offset direction for relation clustering. + // The offset = normalize(target_embed - input_embed) captures + // the RELATION between what activates the feature (entity) + // and what it outputs (target). France→Paris and + // Germany→Berlin share the same offset = "capital-of". + if is_knowledge_layer && top_token_id > 0 && !gate_top_tokens.is_empty() { + let gate_tok = &gate_top_tokens[feat]; + if let Some(offset) = compute_offset_direction( + gate_tok, + top_token_id as usize, + self.weights, + self.tokenizer, + self.hidden_size, + self.vocab_size, + ) { + self.cluster_directions.extend_from_slice(&offset); + self.cluster_features.push((layer, feat)); + let all_tokens: Vec = + top_k_entries.iter().map(|e| e.token.clone()).collect(); + self.cluster_top_tokens.push(all_tokens.join("|")); + self.cluster_input_tokens.push(gate_tok.clone()); + self.cluster_output_tokens.push(top_token.clone()); + } + } + + let feat_idx = feature_offset + feat; + if layer_down_meta.is_none() { + *layer_down_meta = Some(Vec::new()); + } + if let Some(ref mut metas) = layer_down_meta { + while metas.len() <= feat_idx { + metas.push(None); + } + metas[feat_idx] = Some(crate::FeatureMeta { + top_token, + top_token_id, + c_score, + top_k: top_k_entries, + }); + } + } + } + + feature_offset += num_features; + } + + self.callbacks + .on_layer_done(COMP_DOWN, layer, start.elapsed().as_secs_f64() * 1000.0); + } + + crate::format::down_meta::write_binary(self.output_dir, &all_down_meta, self.down_top_k)?; + self.callbacks.on_stage_done(STAGE_DOWN_META, 0.0); + Ok(()) + } + + /// Stage 4 — k-means + label the collected cluster directions. + /// Drains the `cluster_*` accumulators. + fn run_clustering(&mut self) -> Result<(), VindexError> { + run_clustering_pipeline( + ClusterData { + directions: std::mem::take(&mut self.cluster_directions), + features: std::mem::take(&mut self.cluster_features), + top_tokens: std::mem::take(&mut self.cluster_top_tokens), + input_tokens: std::mem::take(&mut self.cluster_input_tokens), + output_tokens: std::mem::take(&mut self.cluster_output_tokens), + }, + self.hidden_size, + self.weights, + self.tokenizer, + self.output_dir, + self.callbacks, + ) + } + + /// Stage 5 — copy the tokenizer JSON. + fn write_tokenizer(&mut self) -> Result<(), VindexError> { + self.callbacks.on_stage(STAGE_TOKENIZER); + let tokenizer_json = self + .tokenizer + .to_string(true) + .map_err(|e| VindexError::Parse(format!("tokenizer serialize: {e}")))?; + std::fs::write(self.output_dir.join(TOKENIZER_JSON), tokenizer_json)?; + self.callbacks.on_stage_done(STAGE_TOKENIZER, 0.0); + Ok(()) + } + + /// Stage 6 — assemble + write `index.json`. If the extract level + /// requires it, also write the model weights and re-emit the index + /// with `has_model_weights = true`. Final pass adds provenance + + /// checksums. + fn write_index_json( + &mut self, + model_name: &str, + extract_level: crate::ExtractLevel, + ) -> Result<(), VindexError> { + let family = self.weights.arch.family().to_string(); + let mut config = VindexConfig { + version: 2, + model: model_name.to_string(), + family: family.clone(), + num_layers: self.num_layers, + hidden_size: self.hidden_size, + intermediate_size: self.intermediate_size, + vocab_size: self.vocab_size, + embed_scale: self.embed_scale, + layers: std::mem::take(&mut self.layer_infos), + down_top_k: self.down_top_k, + has_model_weights: false, + source: None, + checksums: None, + extract_level, + dtype: self.dtype, + quant: crate::QuantFormat::None, + layer_bands: crate::LayerBands::for_family(&family, self.num_layers), + model_config: { + let cfg = self.weights.arch.config(); + Some(VindexModelConfig { + model_type: cfg.model_type.clone(), + head_dim: self.weights.head_dim, + num_q_heads: self.weights.num_q_heads, + num_kv_heads: self.weights.num_kv_heads, + rope_base: self.weights.rope_base, + sliding_window: cfg.sliding_window, + moe: if self.is_moe { + let a = &*self.weights.arch; + Some(crate::MoeConfig { + num_experts: self.n_experts, + top_k: a.num_experts_per_token(), + shared_expert: a.num_shared_experts() > 0, + router_type: a.moe_router_type().to_string(), + moe_intermediate_size: if a.moe_intermediate_size() > 0 { + Some(a.moe_intermediate_size()) + } else { + None + }, + hybrid: a.is_hybrid_moe(), + }) + } else { + None + }, + global_head_dim: cfg.global_head_dim, + num_global_kv_heads: cfg.num_global_kv_heads, + partial_rotary_factor: cfg.partial_rotary_factor, + sliding_window_pattern: cfg.sliding_window_pattern, + layer_types: cfg.layer_types.clone(), + attention_k_eq_v: cfg.attention_k_eq_v, + num_kv_shared_layers: cfg.num_kv_shared_layers, + per_layer_embed_dim: cfg.per_layer_embed_dim, + rope_local_base: cfg.rope_local_base, + query_pre_attn_scalar: cfg.query_pre_attn_scalar, + final_logit_softcapping: cfg.final_logit_softcapping, + }) + }, + fp4: None, + ffn_layout: None, + }; + + // Preliminary write — `write_model_weights` reads the index. + let config_json = + serde_json::to_string_pretty(&config).map_err(|e| VindexError::Parse(e.to_string()))?; + std::fs::write(self.output_dir.join(INDEX_JSON), config_json)?; + + if extract_level != crate::ExtractLevel::Browse { + crate::format::weights::write_model_weights( + self.weights, + self.output_dir, + self.callbacks, + )?; + config.has_model_weights = true; + } + + // Final pass — provenance + checksums. + config.source = Some(crate::VindexSource { + huggingface_repo: Some(model_name.to_string()), + huggingface_revision: None, + safetensors_sha256: None, + extracted_at: chrono_now(), + larql_version: env!("CARGO_PKG_VERSION").to_string(), + }); + config.checksums = crate::format::checksums::compute_checksums(self.output_dir).ok(); + + let config_json = + serde_json::to_string_pretty(&config).map_err(|e| VindexError::Parse(e.to_string()))?; + std::fs::write(self.output_dir.join(INDEX_JSON), config_json)?; + Ok(()) + } +} + +// ═══════════════════════════════════════════════════════════════════════ +// Entry points +// ═══════════════════════════════════════════════════════════════════════ + +/// Build a .vindex from model weights and write it to disk. +/// +/// Reads gate vectors and down projections directly from safetensors, +/// projects down vectors to vocabulary for top-k token metadata, +/// writes everything to a self-contained directory. +#[allow(clippy::too_many_arguments)] +pub fn build_vindex( + weights: &ModelWeights, + tokenizer: &tokenizers::Tokenizer, + model_name: &str, + output_dir: &Path, + down_top_k: usize, + extract_level: crate::ExtractLevel, + dtype: StorageDtype, + callbacks: &mut dyn IndexBuildCallbacks, +) -> Result<(), VindexError> { + std::fs::create_dir_all(output_dir)?; + let mut ctx = BuildContext::new(weights, tokenizer, output_dir, callbacks, dtype, down_top_k); + ctx.write_gate_vectors()?; + ctx.write_embeddings()?; + ctx.write_down_meta_and_clusters()?; + ctx.run_clustering()?; + ctx.write_tokenizer()?; + ctx.write_index_json(model_name, extract_level)?; + Ok(()) +} + +/// Resume an interrupted vindex build. +/// Assumes gate_vectors.bin, embeddings.bin, and down_meta.jsonl exist. +/// Runs: relation clustering + tokenizer + index.json. +pub fn build_vindex_resume( + weights: &ModelWeights, + tokenizer: &tokenizers::Tokenizer, + model_name: &str, + output_dir: &Path, + callbacks: &mut dyn IndexBuildCallbacks, +) -> Result<(), VindexError> { + let num_layers = weights.num_layers; + let hidden_size = weights.hidden_size; + let intermediate_size = weights.intermediate_size; + let vocab_size = weights.vocab_size; + let embed_scale = weights.arch.embed_scale(); + + // Reconstruct layer_infos from gate_vectors.bin + let gate_path = output_dir.join(GATE_VECTORS_BIN); + let gate_size = std::fs::metadata(&gate_path)?.len(); + let bytes_per_layer = (intermediate_size * hidden_size * 4) as u64; + let mut layer_infos = Vec::new(); + for layer in 0..num_layers { + layer_infos.push(VindexLayerInfo { + layer, + num_features: intermediate_size, + offset: layer as u64 * bytes_per_layer, + length: bytes_per_layer, + num_experts: None, + num_features_per_expert: None, + }); + } + eprintln!( + " Reconstructed {} layer infos from gate_vectors.bin ({:.1} GB)", + layer_infos.len(), + gate_size as f64 / 1e9 + ); + + // Read down_meta.jsonl to collect cluster directions (L14-28) + let cluster_layer_min = 14.min(num_layers); + let cluster_layer_max = 28.min(num_layers); + let mut cluster_directions: Vec = Vec::new(); + let mut cluster_features: Vec<(usize, usize)> = Vec::new(); + let mut cluster_top_tokens: Vec = Vec::new(); + let mut cluster_input_tokens: Vec = Vec::new(); + let mut cluster_output_tokens: Vec = Vec::new(); + + eprintln!(" Building whole-word vocabulary..."); + let (ww_ids, ww_embed) = + build_whole_word_vocab(tokenizer, &weights.embed, vocab_size, hidden_size); + + eprintln!( + " Computing gate input tokens for L{}-{}...", + cluster_layer_min, + cluster_layer_max - 1 + ); + let mut gate_top_tokens_per_layer: std::collections::HashMap> = + std::collections::HashMap::new(); + for layer in cluster_layer_min..cluster_layer_max { + let layer_start = std::time::Instant::now(); + let tokens = compute_gate_top_tokens( + weights, + tokenizer, + layer, + intermediate_size, + &ww_ids, + &ww_embed, + ); + gate_top_tokens_per_layer.insert(layer, tokens); + eprintln!( + " gate L{:2}: {:.1}s", + layer, + layer_start.elapsed().as_secs_f64() + ); + } + eprintln!( + " Gate input tokens computed for {} layers", + gate_top_tokens_per_layer.len() + ); + + eprintln!(" Reading down_meta.jsonl for offset directions..."); + let down_path = output_dir.join("down_meta.jsonl"); + let down_file = std::fs::File::open(&down_path)?; + let reader = std::io::BufReader::new(down_file); + let mut count = 0usize; + for line in std::io::BufRead::lines(reader) { + let line = line?; + let line = line.trim(); + if line.is_empty() { + continue; + } + let obj: serde_json::Value = + serde_json::from_str(line).map_err(|e| VindexError::Parse(e.to_string()))?; + if obj.get("_header").is_some() { + continue; + } + + let layer = obj.get("l").and_then(|v| v.as_u64()).unwrap_or(0) as usize; + let feat = obj.get("f").and_then(|v| v.as_u64()).unwrap_or(0) as usize; + let top_token_id = obj.get("i").and_then(|v| v.as_u64()).unwrap_or(0) as usize; + + if layer >= cluster_layer_min + && layer < cluster_layer_max + && top_token_id > 2 + && top_token_id < vocab_size + { + if let Some(gate_tokens) = gate_top_tokens_per_layer.get(&layer) { + if feat < gate_tokens.len() { + let gate_tok = &gate_tokens[feat]; + if let Some(offset) = compute_offset_direction( + gate_tok, + top_token_id, + weights, + tokenizer, + hidden_size, + vocab_size, + ) { + cluster_directions.extend_from_slice(&offset); + cluster_features.push((layer, feat)); + let all_tokens: Vec = obj + .get("k") + .and_then(|v| v.as_array()) + .map(|arr| { + arr.iter() + .filter_map(|e| { + e.get("t").and_then(|t| t.as_str()).map(|s| s.to_string()) + }) + .collect() + }) + .unwrap_or_default(); + cluster_top_tokens.push(all_tokens.join("|")); + let out_str = obj + .get("t") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + cluster_input_tokens.push(gate_tok.clone()); + cluster_output_tokens.push(out_str); + } + } + } + } + count += 1; + if count.is_multiple_of(50000) { + eprint!("\r Read {} features...", count); + } + } + eprintln!( + "\r Read {} features, {} in knowledge layers", + count, + cluster_features.len() + ); + + run_clustering_pipeline( + ClusterData { + directions: cluster_directions, + features: cluster_features, + top_tokens: cluster_top_tokens, + input_tokens: cluster_input_tokens, + output_tokens: cluster_output_tokens, + }, + hidden_size, + weights, + tokenizer, + output_dir, + callbacks, + )?; + + callbacks.on_stage(STAGE_TOKENIZER); + let tokenizer_json = tokenizer + .to_string(true) + .map_err(|e| VindexError::Parse(format!("tokenizer serialize: {e}")))?; + std::fs::write(output_dir.join(TOKENIZER_JSON), tokenizer_json)?; + callbacks.on_stage_done(STAGE_TOKENIZER, 0.0); + + let down_top_k = 10; // default + let family = weights.arch.family().to_string(); + let mut config = VindexConfig { + version: 2, + model: model_name.to_string(), + family: family.clone(), + num_layers, + hidden_size, + intermediate_size, + vocab_size, + embed_scale, + layers: layer_infos, + down_top_k, + has_model_weights: output_dir.join("model_weights.bin").exists(), + source: Some(crate::VindexSource { + huggingface_repo: Some(model_name.to_string()), + huggingface_revision: None, + safetensors_sha256: None, + extracted_at: chrono_now(), + larql_version: env!("CARGO_PKG_VERSION").to_string(), + }), + checksums: None, + extract_level: crate::ExtractLevel::Browse, + dtype: StorageDtype::F32, + quant: crate::QuantFormat::None, + layer_bands: crate::LayerBands::for_family(&family, num_layers), + model_config: { + let cfg = weights.arch.config(); + Some(VindexModelConfig { + model_type: cfg.model_type.clone(), + head_dim: weights.head_dim, + num_q_heads: weights.num_q_heads, + num_kv_heads: weights.num_kv_heads, + rope_base: weights.rope_base, + sliding_window: cfg.sliding_window, + moe: if weights.arch.is_moe() { + Some(crate::MoeConfig { + num_experts: weights.arch.num_experts(), + top_k: weights.arch.num_experts_per_token(), + shared_expert: weights.arch.num_shared_experts() > 0, + router_type: weights.arch.moe_router_type().to_string(), + moe_intermediate_size: if weights.arch.moe_intermediate_size() > 0 { + Some(weights.arch.moe_intermediate_size()) + } else { + None + }, + hybrid: weights.arch.is_hybrid_moe(), + }) + } else { + None + }, + global_head_dim: cfg.global_head_dim, + num_global_kv_heads: cfg.num_global_kv_heads, + partial_rotary_factor: cfg.partial_rotary_factor, + sliding_window_pattern: cfg.sliding_window_pattern, + layer_types: cfg.layer_types.clone(), + attention_k_eq_v: cfg.attention_k_eq_v, + num_kv_shared_layers: cfg.num_kv_shared_layers, + per_layer_embed_dim: cfg.per_layer_embed_dim, + rope_local_base: cfg.rope_local_base, + query_pre_attn_scalar: cfg.query_pre_attn_scalar, + final_logit_softcapping: cfg.final_logit_softcapping, + }) + }, + fp4: None, + ffn_layout: None, + }; + + config.checksums = crate::format::checksums::compute_checksums(output_dir).ok(); + + let config_json = + serde_json::to_string_pretty(&config).map_err(|e| VindexError::Parse(e.to_string()))?; + std::fs::write(output_dir.join(INDEX_JSON), config_json)?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use ndarray::ArcArray2; + use std::collections::HashMap; + use tempfile::TempDir; + + use super::build_vindex; + use crate::{ + ExtractLevel, SilentBuildCallbacks, SilentLoadCallbacks, StorageDtype, VectorIndex, + }; + + // ── synthetic model fixture ────────────────────────────────────────── + + const NUM_LAYERS: usize = 2; + const HIDDEN: usize = 8; + const INTERMEDIATE: usize = 4; + const VOCAB: usize = 16; + + fn make_weights() -> larql_models::ModelWeights { + let mut tensors: HashMap> = HashMap::new(); + let mut vectors: HashMap> = HashMap::new(); + + for layer in 0..NUM_LAYERS { + let mut gate = ndarray::Array2::::zeros((INTERMEDIATE, HIDDEN)); + for i in 0..INTERMEDIATE { + gate[[i, i % HIDDEN]] = 1.0; + } + tensors.insert( + format!("layers.{layer}.mlp.gate_proj.weight"), + gate.into_shared(), + ); + + let mut up = ndarray::Array2::::zeros((INTERMEDIATE, HIDDEN)); + for i in 0..INTERMEDIATE { + up[[i, (i + 1) % HIDDEN]] = 0.5; + } + tensors.insert( + format!("layers.{layer}.mlp.up_proj.weight"), + up.into_shared(), + ); + + let mut down = ndarray::Array2::::zeros((HIDDEN, INTERMEDIATE)); + for i in 0..INTERMEDIATE { + down[[i % HIDDEN, i]] = 0.3; + } + tensors.insert( + format!("layers.{layer}.mlp.down_proj.weight"), + down.into_shared(), + ); + + for suffix in &["q_proj", "k_proj", "v_proj", "o_proj"] { + let mut a = ndarray::Array2::::zeros((HIDDEN, HIDDEN)); + for i in 0..HIDDEN { + a[[i, i]] = 1.0; + } + tensors.insert( + format!("layers.{layer}.self_attn.{suffix}.weight"), + a.into_shared(), + ); + } + vectors.insert( + format!("layers.{layer}.input_layernorm.weight"), + vec![1.0; HIDDEN], + ); + vectors.insert( + format!("layers.{layer}.post_attention_layernorm.weight"), + vec![1.0; HIDDEN], + ); + } + vectors.insert("norm.weight".into(), vec![1.0; HIDDEN]); + + let mut embed = ndarray::Array2::::zeros((VOCAB, HIDDEN)); + for i in 0..VOCAB { + embed[[i, i % HIDDEN]] = 1.0; + } + let embed = embed.into_shared(); + let lm_head = embed.clone(); + + let arch = larql_models::detect_from_json(&serde_json::json!({ + "model_type": "llama", + "hidden_size": HIDDEN, + "num_hidden_layers": NUM_LAYERS, + "intermediate_size": INTERMEDIATE, + "head_dim": HIDDEN, + "num_attention_heads": 1, + "num_key_value_heads": 1, + "rope_theta": 10000.0, + "vocab_size": VOCAB, + })); + larql_models::ModelWeights { + tensors, + vectors, + raw_bytes: HashMap::new(), + skipped_tensors: Vec::new(), + packed_mmaps: HashMap::new(), + packed_byte_ranges: HashMap::new(), + embed, + lm_head, + num_layers: NUM_LAYERS, + hidden_size: HIDDEN, + intermediate_size: INTERMEDIATE, + vocab_size: VOCAB, + head_dim: HIDDEN, + num_q_heads: 1, + num_kv_heads: 1, + rope_base: 10000.0, + arch, + } + } + + const TOK_JSON: &str = + r#"{"version":"1.0","model":{"type":"BPE","vocab":{},"merges":[]},"added_tokens":[]}"#; + + fn tokenizer() -> tokenizers::Tokenizer { + tokenizers::Tokenizer::from_bytes(TOK_JSON).unwrap() + } + + fn run_build(dir: &std::path::Path, level: ExtractLevel, dtype: StorageDtype) { + let weights = make_weights(); + let tok = tokenizer(); + let mut cb = SilentBuildCallbacks; + build_vindex(&weights, &tok, "test/unit", dir, 3, level, dtype, &mut cb).unwrap(); + } + + // ── build output file inventory ────────────────────────────────────── + + #[test] + fn build_browse_writes_required_files() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + assert!( + dir.path().join("gate_vectors.bin").exists(), + "gate_vectors.bin missing" + ); + assert!( + dir.path().join("embeddings.bin").exists(), + "embeddings.bin missing" + ); + assert!( + dir.path().join("down_meta.bin").exists(), + "down_meta.bin missing" + ); + assert!(dir.path().join("index.json").exists(), "index.json missing"); + assert!( + dir.path().join("tokenizer.json").exists(), + "tokenizer.json missing" + ); + } + + #[test] + fn build_browse_does_not_write_weight_files() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + // Browse level: no model weights + assert!(!dir.path().join("attn_weights.bin").exists()); + assert!(!dir.path().join("up_weights.bin").exists()); + assert!(!dir.path().join("down_weights.bin").exists()); + } + + #[test] + fn build_all_writes_weight_files() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::All, StorageDtype::F32); + assert!( + dir.path().join("attn_weights.bin").exists(), + "attn_weights.bin missing" + ); + assert!( + dir.path().join("up_weights.bin").exists(), + "up_weights.bin missing" + ); + assert!( + dir.path().join("down_weights.bin").exists(), + "down_weights.bin missing" + ); + } + + // ── index.json content ─────────────────────────────────────────────── + + #[test] + fn build_index_json_has_correct_shape() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + assert_eq!(cfg.num_layers, NUM_LAYERS); + assert_eq!(cfg.hidden_size, HIDDEN); + assert_eq!(cfg.intermediate_size, INTERMEDIATE); + assert_eq!(cfg.vocab_size, VOCAB); + assert_eq!(cfg.model, "test/unit"); + assert_eq!(cfg.version, 2); + } + + #[test] + fn build_browse_has_model_weights_false() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + assert!(!cfg.has_model_weights); + } + + #[test] + fn build_all_has_model_weights_true() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::All, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + assert!(cfg.has_model_weights); + } + + #[test] + fn build_records_source_provenance() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + let src = cfg.source.unwrap(); + assert_eq!(src.huggingface_repo.as_deref(), Some("test/unit")); + assert!(!src.larql_version.is_empty()); + } + + #[test] + fn build_records_checksums() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + let checksums = cfg.checksums.unwrap(); + assert!( + checksums.contains_key("gate_vectors.bin"), + "gate_vectors.bin not in checksums" + ); + } + + #[test] + fn build_layer_infos_match_num_layers() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + assert_eq!(cfg.layers.len(), NUM_LAYERS); + for (i, info) in cfg.layers.iter().enumerate() { + assert_eq!(info.layer, i, "layer index mismatch at position {i}"); + assert_eq!( + info.num_features, INTERMEDIATE, + "wrong feature count at layer {i}" + ); + } + } + + // ── gate_vectors.bin content ───────────────────────────────────────── + + #[test] + fn build_gate_vectors_bin_size_matches_config() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + let expected: u64 = cfg.layers.iter().map(|l| l.length).sum(); + let actual = std::fs::metadata(dir.path().join("gate_vectors.bin")) + .unwrap() + .len(); + assert_eq!(actual, expected, "gate_vectors.bin size mismatch"); + } + + // ── round-trip: build then load ────────────────────────────────────── + + #[test] + fn build_then_load_vindex_succeeds() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let mut cb = SilentLoadCallbacks; + let index = VectorIndex::load_vindex(dir.path(), &mut cb).unwrap(); + assert_eq!(index.num_layers, NUM_LAYERS); + assert_eq!(index.hidden_size, HIDDEN); + assert_eq!(index.total_gate_vectors(), NUM_LAYERS * INTERMEDIATE); + } + + #[test] + fn build_then_load_gate_knn_returns_results() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let mut cb = SilentLoadCallbacks; + let index = VectorIndex::load_vindex(dir.path(), &mut cb).unwrap(); + let query = ndarray::Array1::from_vec(vec![1.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]); + let hits = index.gate_knn(0, &query, 2); + assert!(!hits.is_empty(), "gate_knn returned no results after build"); + } + + #[test] + fn build_f16_dtype_round_trips() { + let dir = TempDir::new().unwrap(); + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F16); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + assert_eq!(cfg.dtype, StorageDtype::F16); + let mut cb = SilentLoadCallbacks; + let index = VectorIndex::load_vindex(dir.path(), &mut cb).unwrap(); + assert_eq!(index.num_layers, NUM_LAYERS); + } + + #[test] + fn build_idempotent_on_existing_dir() { + let dir = TempDir::new().unwrap(); + // First build + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + // Second build into same directory should overwrite cleanly + run_build(dir.path(), ExtractLevel::Browse, StorageDtype::F32); + let cfg = crate::format::load::load_vindex_config(dir.path()).unwrap(); + assert_eq!(cfg.num_layers, NUM_LAYERS); + } +} From 45167e3163e9db8c7d7870d2434828eb7a6de6b6 Mon Sep 17 00:00:00 2001 From: Priyanshu Rai Date: Wed, 6 May 2026 16:31:09 +0530 Subject: [PATCH 3/3] docs(larql-server): Added Swagger UI for API Documentation --- crates/larql-server/Cargo.toml | 2 + crates/larql-server/src/bootstrap.rs | 12 + crates/larql-server/src/error.rs | 12 +- crates/larql-server/src/lib.rs | 1 + crates/larql-server/src/openapi.rs | 581 ++++++++++++++++++ crates/larql-server/src/routes/describe.rs | 37 +- crates/larql-server/src/routes/embed.rs | 151 ++++- .../src/routes/expert/batch_legacy.rs | 14 + .../src/routes/expert/layer_batch.rs | 29 + crates/larql-server/src/routes/expert/mod.rs | 13 +- .../src/routes/expert/multi_layer_batch.rs | 30 + .../larql-server/src/routes/expert/single.rs | 15 + crates/larql-server/src/routes/explain.rs | 25 +- crates/larql-server/src/routes/health.rs | 8 + crates/larql-server/src/routes/infer.rs | 30 +- crates/larql-server/src/routes/insert.rs | 25 +- crates/larql-server/src/routes/models.rs | 8 + crates/larql-server/src/routes/openai/chat.rs | 14 + .../src/routes/openai/completions.rs | 14 + .../src/routes/openai/embeddings.rs | 12 + crates/larql-server/src/routes/patches.rs | 72 +++ crates/larql-server/src/routes/relations.rs | 27 +- crates/larql-server/src/routes/select.rs | 25 +- crates/larql-server/src/routes/stats.rs | 19 + crates/larql-server/src/routes/stream.rs | 19 + crates/larql-server/src/routes/topology.rs | 12 +- crates/larql-server/src/routes/walk.rs | 32 +- crates/larql-server/src/routes/walk_ffn.rs | 35 ++ crates/larql-server/src/routes/warmup.rs | 14 +- 29 files changed, 1265 insertions(+), 23 deletions(-) create mode 100644 crates/larql-server/src/openapi.rs diff --git a/crates/larql-server/Cargo.toml b/crates/larql-server/Cargo.toml index 0447fd012..1f19e9b4d 100644 --- a/crates/larql-server/Cargo.toml +++ b/crates/larql-server/Cargo.toml @@ -46,6 +46,8 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } thiserror = { workspace = true } base64 = "0.22" +utoipa = { version = "5", features = ["axum_extras", "preserve_order"] } +utoipa-swagger-ui = { version = "9", features = ["axum"] } [features] default = [] diff --git a/crates/larql-server/src/bootstrap.rs b/crates/larql-server/src/bootstrap.rs index 815579521..5201df0c7 100644 --- a/crates/larql-server/src/bootstrap.rs +++ b/crates/larql-server/src/bootstrap.rs @@ -523,6 +523,10 @@ pub struct Cli { #[arg(long)] pub cors: bool, + /// Disable the built-in Swagger UI and /v1/openapi.json endpoint. + #[arg(long)] + pub no_docs: bool, + /// API key for authentication (clients send Authorization: Bearer ). #[arg(long)] pub api_key: Option, @@ -869,6 +873,14 @@ pub async fn serve(cli: Cli) -> Result<(), BoxError> { } } + // OpenAPI / Swagger UI. Mounted before auth so the docs stay reachable + // without the API key — consistent with --cors behavior. Flip the + // ordering if operators want docs gated. + if !cli.no_docs { + app = app.merge(crate::openapi::swagger_router()); + info!("OpenAPI: /swagger-ui and /v1/openapi.json enabled"); + } + // Auth middleware. if cli.api_key.is_some() { app = app.layer(middleware::from_fn_with_state( diff --git a/crates/larql-server/src/error.rs b/crates/larql-server/src/error.rs index 3de32e985..fd8218852 100644 --- a/crates/larql-server/src/error.rs +++ b/crates/larql-server/src/error.rs @@ -2,6 +2,15 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; +use serde::Serialize; +use utoipa::ToSchema; + +/// JSON body returned for every error response. +#[derive(Debug, Serialize, ToSchema)] +pub struct ErrorBody { + /// Human-readable error message. + pub error: String, +} #[derive(Debug, thiserror::Error)] pub enum ServerError { @@ -30,7 +39,6 @@ impl IntoResponse for ServerError { ServerError::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()), }; - let body = serde_json::json!({ "error": message }); - (status, axum::Json(body)).into_response() + (status, axum::Json(ErrorBody { error: message })).into_response() } } diff --git a/crates/larql-server/src/lib.rs b/crates/larql-server/src/lib.rs index bcab84ffc..d97a0177d 100644 --- a/crates/larql-server/src/lib.rs +++ b/crates/larql-server/src/lib.rs @@ -17,6 +17,7 @@ pub mod ffn_l2_cache; pub mod grpc; pub mod grpc_expert; pub mod http; +pub mod openapi; pub mod ratelimit; pub mod routes; pub mod session; diff --git a/crates/larql-server/src/openapi.rs b/crates/larql-server/src/openapi.rs new file mode 100644 index 000000000..73fb9758e --- /dev/null +++ b/crates/larql-server/src/openapi.rs @@ -0,0 +1,581 @@ +//! OpenAPI / Swagger UI aggregation. +//! +//! Spec JSON is served at `/v1/openapi.json` and the browse-friendly +//! Swagger UI at `/swagger-ui`. Both can be disabled with `--no-docs`. +//! +//! Handlers are annotated in place with `#[utoipa::path]`. This module +//! owns: +//! - `ApiDoc` — the aggregator `#[derive(OpenApi)]` struct. +//! - `schemas` — synthetic response structs for handlers that return +//! `Json` (most of the browse/inference surface). +//! - `params` — shared request parameters (e.g. `model_id`). +//! - `swagger_router()` — helper that returns a ready-to-merge router +//! hosting both the UI and the spec JSON. + +use utoipa::OpenApi; +use utoipa_swagger_ui::SwaggerUi; + +use crate::error::ErrorBody; + +pub mod params { + use utoipa::IntoParams; + + /// Path parameter selecting which vindex to target in multi-model mode. + #[derive(IntoParams)] + #[into_params(parameter_in = Path)] + #[allow(dead_code)] + pub struct ModelIdParam { + /// The id of a loaded vindex, e.g. `gemma-3-1b-it`. + pub model_id: String, + } +} + +pub mod schemas { + //! Synthetic response schemas. + //! + //! Populated as each handler group is annotated. Structs here are + //! `Serialize + ToSchema` mirrors of the actual JSON the handlers + //! emit via `Json`. They are never constructed at + //! runtime — they exist purely for spec generation. + + use serde::Serialize; + use utoipa::ToSchema; + + // ---- browse ------------------------------------------------------ + + /// One knowledge edge returned from `/v1/describe`. + #[derive(Serialize, ToSchema)] + pub struct DescribeEdge { + /// Top token at this feature (trimmed). + pub target: String, + /// Gate activation score (rounded to 0.1). + pub gate_score: f32, + /// Layer the feature lives on. + pub layer: usize, + /// Feature index within the layer. + pub feature: usize, + /// Relation label (present when a probe-confirmed label exists). + #[serde(skip_serializing_if = "Option::is_none")] + pub relation: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct DescribeResponse { + pub entity: String, + pub model: String, + pub edges: Vec, + pub latency_ms: f64, + } + + /// One walk hit returned from `/v1/walk`. + #[derive(Serialize, ToSchema)] + pub struct WalkHit { + pub layer: usize, + pub feature: usize, + pub gate_score: f32, + pub target: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub relation: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct WalkResponse { + pub prompt: String, + pub hits: Vec, + pub latency_ms: f64, + } + + #[derive(Serialize, ToSchema)] + pub struct RelationEntry { + pub name: String, + pub count: usize, + pub max_score: f32, + pub min_layer: usize, + pub max_layer: usize, + pub examples: Vec, + } + + #[derive(Serialize, ToSchema)] + pub struct RelationsResponse { + pub relations: Vec, + pub total: usize, + pub latency_ms: f64, + } + + #[derive(Serialize, ToSchema)] + pub struct LayerBands { + pub syntax: [usize; 2], + pub knowledge: [usize; 2], + pub output: [usize; 2], + } + + #[derive(Serialize, ToSchema)] + pub struct LoadedCapabilities { + pub browse: bool, + pub inference: bool, + pub ffn_service: bool, + pub embed_service: bool, + } + + #[derive(Serialize, ToSchema)] + pub struct StatsResponse { + pub model: String, + pub family: String, + pub layers: usize, + pub features: usize, + pub features_per_layer: usize, + pub hidden_size: usize, + pub vocab_size: usize, + pub extract_level: String, + pub dtype: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub layer_bands: Option, + pub loaded: LoadedCapabilities, + } + + /// One entry in the OpenAI-compatible `/v1/models` list. + #[derive(Serialize, ToSchema)] + pub struct ModelEntry { + pub id: String, + pub object: String, + pub created: u64, + pub owned_by: String, + /// Route prefix for this model. `/v1/{id}` in multi-model mode, `/v1` otherwise. + pub path: String, + /// Total features across all layers. + pub features: usize, + pub loaded: bool, + } + + #[derive(Serialize, ToSchema)] + pub struct ModelsListResponse { + pub object: String, + pub data: Vec, + } + + // ---- inference --------------------------------------------------- + + #[derive(Serialize, ToSchema)] + pub struct SelectRow { + pub layer: usize, + pub feature: usize, + pub target: String, + pub confidence: f32, + #[serde(skip_serializing_if = "Option::is_none")] + pub relation: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct SelectResponse { + pub rows: Vec, + pub total: usize, + pub latency_ms: f64, + } + + #[derive(Serialize, ToSchema)] + pub struct Prediction { + pub token: String, + pub probability: f64, + } + + #[derive(Serialize, ToSchema)] + pub struct InferResponse { + pub prompt: String, + pub mode: String, + /// Single-mode (`walk` or `dense`). + #[serde(skip_serializing_if = "Option::is_none")] + pub predictions: Option>, + /// Populated in `compare` mode. + #[serde(skip_serializing_if = "Option::is_none")] + pub walk: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub dense: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub walk_ms: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub dense_ms: Option, + pub latency_ms: f64, + } + + #[derive(Serialize, ToSchema)] + pub struct ExplainLayerEntry { + pub layer: usize, + pub top_features: Vec, + pub top_tokens: Vec<(String, f64)>, + } + + #[derive(Serialize, ToSchema)] + pub struct ExplainResponse { + pub prompt: String, + pub predictions: Vec, + pub layers: Vec, + pub latency_ms: f64, + } + + #[derive(Serialize, ToSchema)] + pub struct InsertResponse { + pub success: bool, + pub entity: String, + pub relation: String, + pub target: String, + pub layers_written: Vec, + pub latency_ms: f64, + } + + // ---- patches ----------------------------------------------------- + + /// Request body for `POST /v1/patches/apply`. Provide either a `url` + /// pointing at a `.vlp` file (local path or `hf://` URL) or an + /// inline `patch` object. One of the two is required. + #[derive(Serialize, ToSchema)] + pub struct ApplyPatchBody { + /// Local path, `http(s)://`, or `hf://` URL to a `.vlp` patch file. + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, + /// Inline patch payload. See VindexPatch docs for schema; includes + /// `description`, `base_model`, and `operations` (INSERT / DELETE). + #[serde(skip_serializing_if = "Option::is_none")] + pub patch: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct ApplyPatchResponse { + pub applied: String, + pub operations: usize, + pub active_patches: usize, + #[serde(skip_serializing_if = "Option::is_none")] + pub session: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct PatchEntry { + pub name: String, + pub operations: usize, + #[serde(skip_serializing_if = "Option::is_none")] + pub base_model: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct ListPatchesResponse { + pub patches: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub session: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct RemovePatchResponse { + pub removed: String, + pub active_patches: usize, + #[serde(skip_serializing_if = "Option::is_none")] + pub session: Option, + } + + // ---- admin ------------------------------------------------------- + + #[derive(Serialize, ToSchema)] + pub struct HealthResponse { + pub status: String, + pub uptime_seconds: u64, + pub requests_served: u64, + } + + #[derive(Serialize, ToSchema)] + pub struct TokenEncodeResponse { + pub token_ids: Vec, + pub text: String, + } + + #[derive(Serialize, ToSchema)] + pub struct TokenDecodeResponse { + pub text: String, + pub token_ids: Vec, + } + + #[derive(Serialize, ToSchema)] + pub struct EmbedSingleJsonResponse { + pub token_id: u32, + pub embedding: Vec, + pub hidden_size: usize, + } + + // ---- openai ------------------------------------------------------ + // + // These mirror the OpenAI wire contract at a high level. + // Full nested types (tools, tool_calls, logprobs, usage) are documented + // inline as open JSON objects to avoid a deep ToSchema tree. + + /// Subset of the OpenAI `POST /v1/embeddings` request body. + #[derive(Serialize, ToSchema)] + pub struct OpenAiEmbeddingsRequest { + /// Model id. Required in multi-model mode; ignored otherwise. + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + /// String, string[], int[] (single sequence), or int[][] (batch of sequences). + pub input: serde_json::Value, + /// `"float"` (default) or `"base64"`. + #[serde(skip_serializing_if = "Option::is_none")] + pub encoding_format: Option, + /// Requested output dimensionality (ignored; returns native hidden size). + #[serde(skip_serializing_if = "Option::is_none")] + pub dimensions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct OpenAiEmbeddingObject { + pub object: String, + pub index: usize, + /// `[f32]` when `encoding_format = "float"`, or a base64 string otherwise. + pub embedding: serde_json::Value, + } + + #[derive(Serialize, ToSchema)] + pub struct OpenAiEmbeddingsResponse { + pub object: String, + pub data: Vec, + pub model: String, + pub usage: serde_json::Value, + } + + /// OpenAI `POST /v1/completions` request. + #[derive(Serialize, ToSchema)] + pub struct OpenAiCompletionsRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + /// Prompt — string or string[]. + pub prompt: serde_json::Value, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub temperature: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub top_p: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub stream: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub n: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub stop: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub echo: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub logprobs: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub seed: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub frequency_penalty: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub presence_penalty: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct OpenAiCompletionsResponse { + pub id: String, + pub object: String, + pub created: u64, + pub model: String, + pub choices: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub usage: Option, + } + + /// OpenAI `POST /v1/chat/completions` request. `messages` is an array + /// of `{role: "system"|"user"|"assistant"|"tool", content, ...}`; tools + /// and structured output are open JSON (see OpenAI docs). + #[derive(Serialize, ToSchema)] + pub struct OpenAiChatRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + pub messages: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub max_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub temperature: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub top_p: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub stream: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub n: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub stop: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub logprobs: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub top_logprobs: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub seed: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub frequency_penalty: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub presence_penalty: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub response_format: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tools: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_choice: Option, + } + + #[derive(Serialize, ToSchema)] + pub struct OpenAiChatResponse { + pub id: String, + pub object: String, + pub created: u64, + pub model: String, + pub choices: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub usage: Option, + } +} + +#[derive(OpenApi)] +#[openapi( + info( + title = "larql-server", + version = env!("CARGO_PKG_VERSION"), + description = "HTTP API for vindex knowledge queries, inference, and remote MoE expert shards.", + ), + tags( + (name = "browse", description = "Knowledge graph browse (no weights required)"), + (name = "inference", description = "Forward passes, explain, insert, warmup"), + (name = "openai", description = "OpenAI-compatible endpoints"), + (name = "expert", description = "Remote MoE shard endpoints (binary wire)"), + (name = "patches", description = "Runtime patch overlay"), + (name = "admin", description = "Health, models, embed, tokens, WebSocket"), + ), + paths( + // browse + crate::routes::describe::handle_describe, + crate::routes::walk::handle_walk, + crate::routes::relations::handle_relations, + crate::routes::stats::handle_stats, + crate::routes::topology::handle_topology, + crate::routes::models::handle_models, + // inference + crate::routes::select::handle_select, + crate::routes::infer::handle_infer, + crate::routes::explain::handle_explain, + crate::routes::insert::handle_insert, + crate::routes::warmup::handle_warmup, + // patches + crate::routes::patches::handle_apply_patch, + crate::routes::patches::handle_list_patches, + crate::routes::patches::handle_remove_patch, + // admin + crate::routes::health::handle_health, + crate::routes::embed::handle_embed, + crate::routes::embed::handle_embed_single, + crate::routes::embed::handle_logits, + crate::routes::embed::handle_token_encode, + crate::routes::embed::handle_token_decode, + crate::routes::stream::handle_stream, + // openai + crate::routes::openai::embeddings::handle_embeddings, + crate::routes::openai::completions::handle_completions, + crate::routes::openai::chat::handle_chat_completions, + // expert + crate::routes::walk_ffn::handle_walk_ffn, + crate::routes::walk_ffn::handle_walk_ffn_q8k, + crate::routes::expert::single::handle_expert, + crate::routes::expert::batch_legacy::handle_expert_batch, + crate::routes::expert::layer_batch::handle_experts_layer_batch, + crate::routes::expert::layer_batch::handle_experts_layer_batch_f16, + crate::routes::expert::multi_layer_batch::handle_experts_multi_layer_batch, + crate::routes::expert::multi_layer_batch::handle_experts_multi_layer_batch_q8k, + // multi-model variants — same handlers with a `{model_id}` path prefix + crate::routes::describe::handle_describe_multi, + crate::routes::walk::handle_walk_multi, + crate::routes::relations::handle_relations_multi, + crate::routes::stats::handle_stats_multi, + crate::routes::select::handle_select_multi, + crate::routes::infer::handle_infer_multi, + crate::routes::explain::handle_explain_multi, + crate::routes::insert::handle_insert_multi, + crate::routes::patches::handle_apply_patch_multi, + crate::routes::patches::handle_list_patches_multi, + crate::routes::patches::handle_remove_patch_multi, + crate::routes::embed::handle_embed_multi, + crate::routes::embed::handle_embed_single_multi, + crate::routes::embed::handle_logits_multi, + crate::routes::embed::handle_token_encode_multi, + crate::routes::embed::handle_token_decode_multi, + ), + components(schemas( + ErrorBody, + // browse + schemas::DescribeEdge, + schemas::DescribeResponse, + schemas::WalkHit, + schemas::WalkResponse, + schemas::RelationEntry, + schemas::RelationsResponse, + schemas::LayerBands, + schemas::LoadedCapabilities, + schemas::StatsResponse, + schemas::ModelEntry, + schemas::ModelsListResponse, + crate::routes::topology::TopologyResponse, + // inference + crate::routes::select::SelectRequest, + schemas::SelectRow, + schemas::SelectResponse, + crate::routes::infer::InferRequest, + schemas::Prediction, + schemas::InferResponse, + crate::routes::explain::ExplainRequest, + schemas::ExplainLayerEntry, + schemas::ExplainResponse, + crate::routes::insert::InsertRequest, + schemas::InsertResponse, + crate::routes::warmup::WarmupRequest, + crate::routes::warmup::WarmupResponse, + // patches + schemas::ApplyPatchBody, + schemas::ApplyPatchResponse, + schemas::PatchEntry, + schemas::ListPatchesResponse, + schemas::RemovePatchResponse, + // admin + schemas::HealthResponse, + schemas::TokenEncodeResponse, + schemas::TokenDecodeResponse, + schemas::EmbedSingleJsonResponse, + crate::routes::embed::EmbedRequest, + crate::routes::embed::EmbedResponse, + crate::routes::embed::LogitsRequest, + crate::routes::embed::LogitsResponse, + crate::routes::embed::TokenProb, + // openai + schemas::OpenAiEmbeddingsRequest, + schemas::OpenAiEmbeddingObject, + schemas::OpenAiEmbeddingsResponse, + schemas::OpenAiCompletionsRequest, + schemas::OpenAiCompletionsResponse, + schemas::OpenAiChatRequest, + schemas::OpenAiChatResponse, + // expert + crate::routes::expert::SingleExpertRequest, + crate::routes::expert::SingleExpertResponse, + crate::routes::expert::BatchExpertItem, + crate::routes::expert::BatchExpertRequest, + crate::routes::expert::BatchExpertResult, + crate::routes::expert::BatchExpertResponse, + )), +)] +pub struct ApiDoc; + +/// Build a router hosting Swagger UI at `/swagger-ui` and the spec at +/// `/v1/openapi.json`. Merge into the main app router. +pub fn swagger_router() -> axum::Router { + SwaggerUi::new("/swagger-ui") + .url("/v1/openapi.json", ApiDoc::openapi()) + .into() +} diff --git a/crates/larql-server/src/routes/describe.rs b/crates/larql-server/src/routes/describe.rs index 77b696864..2af653ede 100644 --- a/crates/larql-server/src/routes/describe.rs +++ b/crates/larql-server/src/routes/describe.rs @@ -18,15 +18,21 @@ use crate::state::{elapsed_ms, AppState, LoadedModel}; const DESCRIBE_CACHE_CONTROL: &str = "public, max-age=86400"; -#[derive(Deserialize)] +#[derive(Deserialize, utoipa::IntoParams)] +#[into_params(parameter_in = Query)] pub struct DescribeParams { + /// Entity to describe, e.g. `France`. pub entity: String, + /// Layer band to scan: `knowledge` (default), `syntax`, `output`, or `all`. #[serde(default = "default_band")] pub band: String, + /// Include low-score edges in the response. #[serde(default)] pub verbose: bool, + /// Maximum number of edges to return. #[serde(default = "default_limit")] pub limit: usize, + /// Minimum gate score to include an edge. #[serde(default = "default_min_score")] pub min_score: f32, } @@ -257,6 +263,19 @@ async fn describe_with_cache( .into_response()) } +#[utoipa::path( + get, + path = "/v1/describe", + tag = "browse", + params(DescribeParams), + responses( + (status = 200, description = "Edges for the queried entity", body = crate::openapi::schemas::DescribeResponse), + (status = 304, description = "Not modified (ETag match)"), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_describe( State(state): State>, headers: HeaderMap, @@ -267,6 +286,22 @@ pub async fn handle_describe( describe_with_cache(&state, model, &headers, params).await } +#[utoipa::path( + get, + path = "/v1/{model_id}/describe", + tag = "browse", + params( + ("model_id" = String, Path, description = "Id of a loaded vindex."), + DescribeParams, + ), + responses( + (status = 200, body = crate::openapi::schemas::DescribeResponse), + (status = 304), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_describe_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/embed.rs b/crates/larql-server/src/routes/embed.rs index 605c35968..f463f635e 100644 --- a/crates/larql-server/src/routes/embed.rs +++ b/crates/larql-server/src/routes/embed.rs @@ -15,6 +15,7 @@ use axum::http::header; use axum::response::{IntoResponse, Response}; use axum::Json; use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; use larql_inference::forward::predict::logits_to_predictions_pub; use larql_vindex::ndarray::Array2; @@ -28,12 +29,12 @@ use crate::state::{AppState, LoadedModel}; // ── Request / response types ────────────────────────────────────────────────── -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct EmbedRequest { pub token_ids: Vec, } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct EmbedResponse { /// Row-major: seq_len × hidden_size f32 values. pub residual: Vec>, @@ -42,7 +43,7 @@ pub struct EmbedResponse { pub latency_ms: f32, } -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct LogitsRequest { /// Flat f32 residual of length hidden_size (one position, post-all-layers). pub residual: Vec, @@ -105,14 +106,14 @@ fn parse_binary_logits_request(bytes: &[u8]) -> Result, ServerError> { .collect()) } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct TokenProb { pub token_id: u32, pub token: String, pub prob: f32, } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct LogitsResponse { pub top_k: Vec, pub latency_ms: f32, @@ -183,6 +184,25 @@ pub(crate) fn embed_tokens( /// /// JSON response: `{"residual": [[f32, ...], ...], "seq_len": N, ...}`. /// Binary response: seq_len×hidden_size f32 LE, prefixed by two u32 headers. +#[utoipa::path( + post, + path = "/v1/embed", + tag = "admin", + request_body( + content = EmbedRequest, + description = "JSON `{token_ids: [u32]}` OR binary `application/x-larql-ffn`: \ + `[num_tokens u32 LE][token_ids u32 LE...]`.", + ), + responses( + (status = 200, description = "JSON response", body = EmbedResponse), + (status = 200, content_type = "application/x-larql-ffn", + body = Vec, + description = "Binary response when the request used `Content-Type: application/x-larql-ffn`: \ + `[seq_len u32][hidden u32][seq_len × hidden f32 LE]`."), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_embed( State(state): State>, headers: axum::http::HeaderMap, @@ -191,6 +211,19 @@ pub async fn handle_embed( handle_embed_inner(&state, None, headers, body).await } +#[utoipa::path( + post, + path = "/v1/{model_id}/embed", + tag = "admin", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + request_body(content = EmbedRequest, description = "JSON or binary `application/x-larql-ffn`."), + responses( + (status = 200, body = EmbedResponse), + (status = 200, content_type = "application/x-larql-ffn", body = Vec), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_embed_multi( State(state): State>, Path(model_id): Path, @@ -281,6 +314,21 @@ async fn handle_embed_inner( /// Accepts JSON (`{"residual": [...], "top_k": 5, "temperature": 1.0}`) or /// binary (`Content-Type: application/x-larql-ffn`, raw hidden_size f32 LE /// bytes). Returns JSON top-k tokens. +#[utoipa::path( + post, + path = "/v1/logits", + tag = "admin", + request_body( + content = LogitsRequest, + description = "JSON `{residual: [f32; hidden_size], top_k, temperature}` OR binary \ + `application/x-larql-ffn` with raw hidden_size f32 LE bytes.", + ), + responses( + (status = 200, description = "Top-K tokens from lm_head", body = LogitsResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_logits( State(state): State>, headers: axum::http::HeaderMap, @@ -289,6 +337,18 @@ pub async fn handle_logits( handle_logits_inner(&state, None, headers, body).await } +#[utoipa::path( + post, + path = "/v1/{model_id}/logits", + tag = "admin", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + request_body(content = LogitsRequest), + responses( + (status = 200, body = LogitsResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_logits_multi( State(state): State>, Path(model_id): Path, @@ -380,6 +440,18 @@ async fn handle_logits_inner( // ───────────────────────────────────────────────────────────────────────────── /// `GET /v1/token/encode?text=Paris` +#[utoipa::path( + get, + path = "/v1/token/encode", + tag = "admin", + params( + ("text" = String, Query, description = "Text to tokenize."), + ), + responses( + (status = 200, description = "Token IDs for the text", body = crate::openapi::schemas::TokenEncodeResponse), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_token_encode( State(state): State>, Query(q): Query, @@ -387,6 +459,19 @@ pub async fn handle_token_encode( handle_token_encode_inner(&state, None, q) } +#[utoipa::path( + get, + path = "/v1/{model_id}/token/encode", + tag = "admin", + params( + ("model_id" = String, Path, description = "Id of a loaded vindex."), + ("text" = String, Query, description = "Text to tokenize."), + ), + responses( + (status = 200, body = crate::openapi::schemas::TokenEncodeResponse), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_token_encode_multi( State(state): State>, Path(model_id): Path, @@ -418,6 +503,19 @@ fn handle_token_encode_inner( // ───────────────────────────────────────────────────────────────────────────── /// `GET /v1/token/decode?ids=9515,235,1234` +#[utoipa::path( + get, + path = "/v1/token/decode", + tag = "admin", + params( + ("ids" = String, Query, description = "Comma-separated token IDs, e.g. `9515,235,1234`."), + ), + responses( + (status = 200, description = "Decoded text", body = crate::openapi::schemas::TokenDecodeResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_token_decode( State(state): State>, Query(q): Query, @@ -425,6 +523,20 @@ pub async fn handle_token_decode( handle_token_decode_inner(&state, None, q) } +#[utoipa::path( + get, + path = "/v1/{model_id}/token/decode", + tag = "admin", + params( + ("model_id" = String, Path, description = "Id of a loaded vindex."), + ("ids" = String, Query, description = "Comma-separated token IDs."), + ), + responses( + (status = 200, body = crate::openapi::schemas::TokenDecodeResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_token_decode_multi( State(state): State>, Path(model_id): Path, @@ -482,6 +594,20 @@ fn handle_token_decode_inner( /// /// Response (JSON, if Accept: application/json): /// {"token_id": N, "embedding": [f32, ...], "hidden_size": N} +#[utoipa::path( + get, + path = "/v1/embed/{token_id}", + tag = "admin", + params( + ("token_id" = u32, Path, description = "Vocabulary token id."), + ), + responses( + (status = 200, description = "Binary f32 LE embedding (default)", content_type = "application/x-larql-ffn", body = Vec), + (status = 200, description = "JSON response when `Accept: application/json`", body = crate::openapi::schemas::EmbedSingleJsonResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_embed_single( State(state): State>, Path(token_id): Path, @@ -490,6 +616,21 @@ pub async fn handle_embed_single( handle_embed_single_inner(&state, None, token_id, headers) } +#[utoipa::path( + get, + path = "/v1/{model_id}/embed/{token_id}", + tag = "admin", + params( + ("model_id" = String, Path, description = "Id of a loaded vindex."), + ("token_id" = u32, Path), + ), + responses( + (status = 200, content_type = "application/x-larql-ffn", body = Vec), + (status = 200, body = crate::openapi::schemas::EmbedSingleJsonResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_embed_single_multi( State(state): State>, Path((model_id, token_id)): Path<(String, u32)>, diff --git a/crates/larql-server/src/routes/expert/batch_legacy.rs b/crates/larql-server/src/routes/expert/batch_legacy.rs index a5a90e0cc..ee987129d 100644 --- a/crates/larql-server/src/routes/expert/batch_legacy.rs +++ b/crates/larql-server/src/routes/expert/batch_legacy.rs @@ -28,6 +28,20 @@ use crate::state::AppState; use super::single::run_expert; use super::{BatchExpertRequest, BatchExpertResponse, BatchExpertResult}; +#[utoipa::path( + post, + path = "/v1/expert/batch", + tag = "expert", + request_body( + content = crate::routes::expert::BatchExpertRequest, + description = "JSON `{requests:[{layer, expert_id, residual}]}` OR binary \ + `application/x-larql-expert` wire (see docs/server-spec.md).", + ), + responses( + (status = 200, description = "Per-item expert outputs", body = crate::routes::expert::BatchExpertResponse), + (status = 400, body = crate::error::ErrorBody), + ), +)] pub async fn handle_expert_batch( State(state): State>, headers: axum::http::HeaderMap, diff --git a/crates/larql-server/src/routes/expert/layer_batch.rs b/crates/larql-server/src/routes/expert/layer_batch.rs index 492471c7c..79e3ac719 100644 --- a/crates/larql-server/src/routes/expert/layer_batch.rs +++ b/crates/larql-server/src/routes/expert/layer_batch.rs @@ -57,6 +57,21 @@ fn compute_semaphore() -> &'static Semaphore { }) } +#[utoipa::path( + post, + path = "/v1/experts/layer-batch", + tag = "expert", + request_body( + content_type = "application/octet-stream", + description = "Binary wire: one pre-norm residual + K (expert_id, weight) pairs for a single layer. \ + Router-weighted sum is returned as f32. See `docs/server-spec.md` for the exact packed layout.", + ), + responses( + (status = 200, content_type = "application/x-larql-ffn", + description = "Weighted-sum f32 output", body = Vec), + (status = 400, body = crate::error::ErrorBody), + ), +)] pub async fn handle_experts_layer_batch( State(state): State>, body: Bytes, @@ -123,6 +138,20 @@ pub async fn handle_experts_layer_batch( Ok(resp) } +#[utoipa::path( + post, + path = "/v1/experts/layer-batch-f16", + tag = "expert", + request_body( + content_type = "application/octet-stream", + description = "Same shape as `/v1/experts/layer-batch` but residual is f16 to cut upload bandwidth by 2×.", + ), + responses( + (status = 200, content_type = "application/x-larql-ffn", + description = "Weighted-sum f32 output", body = Vec), + (status = 400, body = crate::error::ErrorBody), + ), +)] pub async fn handle_experts_layer_batch_f16( State(state): State>, body: Bytes, diff --git a/crates/larql-server/src/routes/expert/mod.rs b/crates/larql-server/src/routes/expert/mod.rs index 5fd01da55..a5fc5e60e 100644 --- a/crates/larql-server/src/routes/expert/mod.rs +++ b/crates/larql-server/src/routes/expert/mod.rs @@ -25,6 +25,7 @@ //! Metal expert buffer cache, called from boot. use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; pub mod batch_legacy; pub mod cpu; @@ -57,37 +58,37 @@ pub use warmup::warmup_metal_expert_cache; // Kept in `mod.rs` because they're shared across the single + batch_legacy // handlers and trivially small. -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct SingleExpertRequest { pub residual: Vec, } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct SingleExpertResponse { pub output: Vec, pub latency_ms: f64, } -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct BatchExpertItem { pub layer: usize, pub expert_id: usize, pub residual: Vec, } -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct BatchExpertRequest { pub requests: Vec, } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct BatchExpertResult { pub layer: usize, pub expert_id: usize, pub output: Vec, } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct BatchExpertResponse { pub results: Vec, pub latency_ms: f64, diff --git a/crates/larql-server/src/routes/expert/multi_layer_batch.rs b/crates/larql-server/src/routes/expert/multi_layer_batch.rs index 1f8de791f..675605151 100644 --- a/crates/larql-server/src/routes/expert/multi_layer_batch.rs +++ b/crates/larql-server/src/routes/expert/multi_layer_batch.rs @@ -29,6 +29,21 @@ use crate::state::AppState; use super::cpu::{run_experts_cpu_batch, run_experts_cpu_batch_q8k_prenormed}; +#[utoipa::path( + post, + path = "/v1/experts/multi-layer-batch", + tag = "expert", + request_body( + content_type = "application/octet-stream", + description = "N packed layer tasks — each `(layer, residual, expert_ids, weights)`. \ + Server runs every task in parallel via rayon and returns N per-layer f32 outputs in one response.", + ), + responses( + (status = 200, content_type = "application/x-larql-ffn-multi", + description = "N per-layer f32 outputs, one per task", body = Vec), + (status = 400, body = crate::error::ErrorBody), + ), +)] pub async fn handle_experts_multi_layer_batch( State(state): State>, body: Bytes, @@ -85,6 +100,21 @@ pub async fn handle_experts_multi_layer_batch( /// Q8K-prenormed variant: client pre-quantises h_norm, server skips /// `pre_experts_norm` and `quantize_h_norm_for_q4k` — just the matvec. /// 4× smaller upload; response is standard f32. +#[utoipa::path( + post, + path = "/v1/experts/multi-layer-batch-q8k", + tag = "expert", + request_body( + content_type = "application/octet-stream", + description = "Same shape as `/v1/experts/multi-layer-batch` but the client has already applied `pre_experts_norm` \ + and quantised `h_norm` to Q8K, saving ~4× upload bandwidth. Response is standard f32.", + ), + responses( + (status = 200, content_type = "application/x-larql-ffn-multi", + description = "N per-layer f32 outputs, one per task", body = Vec), + (status = 400, body = crate::error::ErrorBody), + ), +)] pub async fn handle_experts_multi_layer_batch_q8k( State(state): State>, body: Bytes, diff --git a/crates/larql-server/src/routes/expert/single.rs b/crates/larql-server/src/routes/expert/single.rs index 33508888e..48a8b901b 100644 --- a/crates/larql-server/src/routes/expert/single.rs +++ b/crates/larql-server/src/routes/expert/single.rs @@ -137,6 +137,21 @@ pub fn run_expert( Ok(output) } +#[utoipa::path( + post, + path = "/v1/expert/{layer}/{expert_id}", + tag = "expert", + params( + ("layer" = usize, Path), + ("expert_id" = usize, Path), + ), + request_body = SingleExpertRequest, + responses( + (status = 200, description = "Expert output vector", body = SingleExpertResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_expert( State(state): State>, Path((layer, expert_id)): Path<(usize, usize)>, diff --git a/crates/larql-server/src/routes/explain.rs b/crates/larql-server/src/routes/explain.rs index 93cc92c83..dd882a65b 100644 --- a/crates/larql-server/src/routes/explain.rs +++ b/crates/larql-server/src/routes/explain.rs @@ -10,7 +10,7 @@ use crate::band_utils::{get_layer_bands, BAND_KNOWLEDGE, BAND_OUTPUT, BAND_SYNTA use crate::error::ServerError; use crate::state::{elapsed_ms, AppState, LoadedModel}; -#[derive(Deserialize)] +#[derive(Deserialize, utoipa::ToSchema)] pub struct ExplainRequest { pub prompt: String, #[serde(default = "default_top")] @@ -287,6 +287,17 @@ fn explain_infer( Ok(body) } +#[utoipa::path( + post, + path = "/v1/explain-infer", + tag = "inference", + request_body = ExplainRequest, + responses( + (status = 200, description = "Predictions with per-layer feature traces", body = crate::openapi::schemas::ExplainResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_explain( State(state): State>, Json(req): Json, @@ -299,6 +310,18 @@ pub async fn handle_explain( Ok(Json(result)) } +#[utoipa::path( + post, + path = "/v1/{model_id}/explain-infer", + tag = "inference", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + request_body = ExplainRequest, + responses( + (status = 200, body = crate::openapi::schemas::ExplainResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_explain_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/health.rs b/crates/larql-server/src/routes/health.rs index 3f776905b..20916a5bb 100644 --- a/crates/larql-server/src/routes/health.rs +++ b/crates/larql-server/src/routes/health.rs @@ -8,6 +8,14 @@ use axum::Json; use crate::band_utils::HEALTH_STATUS_OK; use crate::state::AppState; +#[utoipa::path( + get, + path = "/v1/health", + tag = "admin", + responses( + (status = 200, description = "Server is alive", body = crate::openapi::schemas::HealthResponse), + ), +)] pub async fn handle_health(State(state): State>) -> Json { state.bump_requests(); let uptime = state.started_at.elapsed().as_secs(); diff --git a/crates/larql-server/src/routes/infer.rs b/crates/larql-server/src/routes/infer.rs index 519751934..2bdbffb06 100644 --- a/crates/larql-server/src/routes/infer.rs +++ b/crates/larql-server/src/routes/infer.rs @@ -12,11 +12,14 @@ use crate::error::ServerError; use crate::session::extract_session_id; use crate::state::{elapsed_ms, AppState, LoadedModel}; -#[derive(Deserialize)] +#[derive(Deserialize, utoipa::ToSchema)] pub struct InferRequest { + /// Prompt to run inference on. pub prompt: String, + /// Top-K next-token predictions to return. #[serde(default = "default_top")] pub top: usize, + /// Inference mode: `walk` (default), `dense`, or `compare`. #[serde(default = "default_mode")] pub mode: String, } @@ -187,6 +190,18 @@ fn run_infer( Ok(serde_json::Value::Object(result)) } +#[utoipa::path( + post, + path = "/v1/infer", + tag = "inference", + request_body = InferRequest, + responses( + (status = 200, description = "Next-token predictions", body = crate::openapi::schemas::InferResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 503, body = crate::error::ErrorBody, description = "Inference weights unavailable"), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_infer( State(state): State>, headers: HeaderMap, @@ -203,6 +218,19 @@ pub async fn handle_infer( Ok(Json(result)) } +#[utoipa::path( + post, + path = "/v1/{model_id}/infer", + tag = "inference", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + request_body = InferRequest, + responses( + (status = 200, body = crate::openapi::schemas::InferResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + (status = 503, body = crate::error::ErrorBody), + ), +)] pub async fn handle_infer_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/insert.rs b/crates/larql-server/src/routes/insert.rs index 5d692a62d..7b80cfad3 100644 --- a/crates/larql-server/src/routes/insert.rs +++ b/crates/larql-server/src/routes/insert.rs @@ -16,7 +16,7 @@ use crate::error::ServerError; use crate::session::extract_session_id; use crate::state::{elapsed_ms, AppState, LoadedModel}; -#[derive(Deserialize)] +#[derive(Deserialize, utoipa::ToSchema)] pub struct InsertRequest { pub entity: String, pub relation: String, @@ -237,6 +237,17 @@ fn run_insert( })) } +#[utoipa::path( + post, + path = "/v1/insert", + tag = "inference", + request_body = InsertRequest, + responses( + (status = 200, description = "Constellation insert result", body = crate::openapi::schemas::InsertResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_insert( State(state): State>, headers: HeaderMap, @@ -253,6 +264,18 @@ pub async fn handle_insert( Ok(Json(result)) } +#[utoipa::path( + post, + path = "/v1/{model_id}/insert", + tag = "inference", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + request_body = InsertRequest, + responses( + (status = 200, body = crate::openapi::schemas::InsertResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_insert_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/models.rs b/crates/larql-server/src/routes/models.rs index 6dd8491d1..8936f647f 100644 --- a/crates/larql-server/src/routes/models.rs +++ b/crates/larql-server/src/routes/models.rs @@ -48,6 +48,14 @@ fn server_boot_unix_secs(state: &AppState) -> u64 { now_unix.saturating_sub(uptime) } +#[utoipa::path( + get, + path = "/v1/models", + tag = "browse", + responses( + (status = 200, description = "OpenAI-compatible list of loaded models", body = crate::openapi::schemas::ModelsListResponse), + ), +)] pub async fn handle_models(State(state): State>) -> Json { state.bump_requests(); diff --git a/crates/larql-server/src/routes/openai/chat.rs b/crates/larql-server/src/routes/openai/chat.rs index 2cd9fe27e..6951e5ec0 100644 --- a/crates/larql-server/src/routes/openai/chat.rs +++ b/crates/larql-server/src/routes/openai/chat.rs @@ -225,6 +225,20 @@ pub struct ChatCompletionsResponse { pub usage: ChatUsage, } +#[utoipa::path( + post, + path = "/v1/chat/completions", + tag = "openai", + request_body = crate::openapi::schemas::OpenAiChatRequest, + responses( + (status = 200, description = "Non-streaming JSON response.", + body = crate::openapi::schemas::OpenAiChatResponse), + (status = 200, description = "SSE stream when `stream: true`. Each event is `data: \\n\\n`, terminated by `data: [DONE]`.", + content_type = "text/event-stream", body = String), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_chat_completions( State(state): State>, Json(req): Json, diff --git a/crates/larql-server/src/routes/openai/completions.rs b/crates/larql-server/src/routes/openai/completions.rs index 46afbd0e1..2f71afc52 100644 --- a/crates/larql-server/src/routes/openai/completions.rs +++ b/crates/larql-server/src/routes/openai/completions.rs @@ -163,6 +163,20 @@ pub struct CompletionsResponse { pub usage: CompletionsUsage, } +#[utoipa::path( + post, + path = "/v1/completions", + tag = "openai", + request_body = crate::openapi::schemas::OpenAiCompletionsRequest, + responses( + (status = 200, description = "Non-streaming JSON response.", + body = crate::openapi::schemas::OpenAiCompletionsResponse), + (status = 200, description = "SSE stream when `stream: true`. Each event is `data: \\n\\n`, terminated by `data: [DONE]`.", + content_type = "text/event-stream", body = String), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_completions( State(state): State>, Json(req): Json, diff --git a/crates/larql-server/src/routes/openai/embeddings.rs b/crates/larql-server/src/routes/openai/embeddings.rs index 85aa49a8b..dfba9ae55 100644 --- a/crates/larql-server/src/routes/openai/embeddings.rs +++ b/crates/larql-server/src/routes/openai/embeddings.rs @@ -118,6 +118,18 @@ pub struct EmbeddingsResponse { pub usage: EmbeddingsUsage, } +#[utoipa::path( + post, + path = "/v1/embeddings", + tag = "openai", + request_body = crate::openapi::schemas::OpenAiEmbeddingsRequest, + responses( + (status = 200, description = "Mean-pooled embeddings (not contrastively trained — use at your own risk).", + body = crate::openapi::schemas::OpenAiEmbeddingsResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_embeddings( State(state): State>, Json(req): Json, diff --git a/crates/larql-server/src/routes/patches.rs b/crates/larql-server/src/routes/patches.rs index 5c70439b1..f58c7bc9d 100644 --- a/crates/larql-server/src/routes/patches.rs +++ b/crates/larql-server/src/routes/patches.rs @@ -163,6 +163,21 @@ async fn apply_patch_to_model( } } +#[utoipa::path( + post, + path = "/v1/patches/apply", + tag = "patches", + request_body( + content = crate::openapi::schemas::ApplyPatchBody, + description = "Provide either a `url` (path / http(s):// / hf://) or an inline `patch` object. \ + Use the `X-Session-Id` header to scope the apply to a session.", + ), + responses( + (status = 200, description = "Patch applied", body = crate::openapi::schemas::ApplyPatchResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_apply_patch( State(state): State>, headers: HeaderMap, @@ -172,6 +187,18 @@ pub async fn handle_apply_patch( apply_patch_to_model(&state, None, &headers, req).await } +#[utoipa::path( + post, + path = "/v1/{model_id}/patches/apply", + tag = "patches", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + request_body(content = crate::openapi::schemas::ApplyPatchBody), + responses( + (status = 200, body = crate::openapi::schemas::ApplyPatchResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_apply_patch_multi( State(state): State>, Path(model_id): Path, @@ -214,6 +241,16 @@ async fn list_patches_for_model( Ok(Json(serde_json::json!({ "patches": patches }))) } +#[utoipa::path( + get, + path = "/v1/patches", + tag = "patches", + responses( + (status = 200, description = "Active patches for the current session or global state", + body = crate::openapi::schemas::ListPatchesResponse), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_list_patches( State(state): State>, headers: HeaderMap, @@ -222,6 +259,16 @@ pub async fn handle_list_patches( list_patches_for_model(&state, None, &headers).await } +#[utoipa::path( + get, + path = "/v1/{model_id}/patches", + tag = "patches", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + responses( + (status = 200, body = crate::openapi::schemas::ListPatchesResponse), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_list_patches_multi( State(state): State>, Path(model_id): Path, @@ -268,6 +315,18 @@ async fn remove_patch_from_model( }))) } +#[utoipa::path( + delete, + path = "/v1/patches/{name}", + tag = "patches", + params( + ("name" = String, Path, description = "Patch description/name (or `inline-patch` if it was inlined without one)."), + ), + responses( + (status = 200, description = "Patch removed", body = crate::openapi::schemas::RemovePatchResponse), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_remove_patch( State(state): State>, headers: HeaderMap, @@ -277,6 +336,19 @@ pub async fn handle_remove_patch( remove_patch_from_model(&state, None, &headers, &name).await } +#[utoipa::path( + delete, + path = "/v1/{model_id}/patches/{name}", + tag = "patches", + params( + ("model_id" = String, Path, description = "Id of a loaded vindex."), + ("name" = String, Path, description = "Patch description/name."), + ), + responses( + (status = 200, body = crate::openapi::schemas::RemovePatchResponse), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_remove_patch_multi( State(state): State>, Path((model_id, name)): Path<(String, String)>, diff --git a/crates/larql-server/src/routes/relations.rs b/crates/larql-server/src/routes/relations.rs index 32aa8b839..15deceeae 100644 --- a/crates/larql-server/src/routes/relations.rs +++ b/crates/larql-server/src/routes/relations.rs @@ -136,7 +136,8 @@ fn is_content_token(tok: &str) -> bool { ) } -#[derive(Deserialize, Default)] +#[derive(Deserialize, Default, utoipa::IntoParams)] +#[into_params(parameter_in = Query)] pub struct RelationsParams { /// Filter by label source (future use). #[serde(default)] @@ -250,6 +251,16 @@ fn list_relations(model: &LoadedModel) -> Result })) } +#[utoipa::path( + get, + path = "/v1/relations", + tag = "browse", + params(RelationsParams), + responses( + (status = 200, description = "Known relation types across the knowledge band", body = crate::openapi::schemas::RelationsResponse), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_relations( State(state): State>, Query(_params): Query, @@ -262,6 +273,20 @@ pub async fn handle_relations( Ok(Json(result)) } +#[utoipa::path( + get, + path = "/v1/{model_id}/relations", + tag = "browse", + params( + ("model_id" = String, Path, description = "Id of a loaded vindex."), + RelationsParams, + ), + responses( + (status = 200, body = crate::openapi::schemas::RelationsResponse), + (status = 404, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_relations_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/select.rs b/crates/larql-server/src/routes/select.rs index 495ee5e76..f26292fd1 100644 --- a/crates/larql-server/src/routes/select.rs +++ b/crates/larql-server/src/routes/select.rs @@ -9,7 +9,7 @@ use serde::Deserialize; use crate::error::ServerError; use crate::state::{elapsed_ms, AppState, LoadedModel}; -#[derive(Deserialize)] +#[derive(Deserialize, utoipa::ToSchema)] pub struct SelectRequest { #[serde(default)] pub entity: Option, @@ -157,6 +157,17 @@ fn select_edges( })) } +#[utoipa::path( + post, + path = "/v1/select", + tag = "inference", + request_body = SelectRequest, + responses( + (status = 200, description = "Selected edges", body = crate::openapi::schemas::SelectResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_select( State(state): State>, Json(req): Json, @@ -169,6 +180,18 @@ pub async fn handle_select( Ok(Json(result)) } +#[utoipa::path( + post, + path = "/v1/{model_id}/select", + tag = "inference", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + request_body = SelectRequest, + responses( + (status = 200, body = crate::openapi::schemas::SelectResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_select_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/stats.rs b/crates/larql-server/src/routes/stats.rs index 545abb2a9..dbe2f0c69 100644 --- a/crates/larql-server/src/routes/stats.rs +++ b/crates/larql-server/src/routes/stats.rs @@ -79,6 +79,15 @@ async fn add_q4k_ffn(model: &LoadedModel, mut stats: serde_json::Value) -> serde stats } +#[utoipa::path( + get, + path = "/v1/stats", + tag = "browse", + responses( + (status = 200, description = "Model + vindex statistics", body = crate::openapi::schemas::StatsResponse), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_stats( State(state): State>, ) -> Result, ServerError> { @@ -88,6 +97,16 @@ pub async fn handle_stats( Ok(Json(add_q4k_ffn(model, stats).await)) } +#[utoipa::path( + get, + path = "/v1/{model_id}/stats", + tag = "browse", + params(("model_id" = String, Path, description = "Id of a loaded vindex.")), + responses( + (status = 200, body = crate::openapi::schemas::StatsResponse), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_stats_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/stream.rs b/crates/larql-server/src/routes/stream.rs index c28cb7ea7..fb8b805f2 100644 --- a/crates/larql-server/src/routes/stream.rs +++ b/crates/larql-server/src/routes/stream.rs @@ -101,6 +101,25 @@ fn ws_infer_done( }) } +#[utoipa::path( + get, + path = "/v1/stream", + tag = "admin", + responses( + (status = 101, description = "\ +WebSocket upgrade. After upgrade:\n\n\ +**Client → server** (text frames, JSON):\n\ +- `{\"type\":\"describe\", \"entity\":\"France\", \"band\":\"all\"}` — streams layer-by-layer describe.\n\ +- `{\"type\":\"infer\", \"prompt\":\"The capital of France is\", \"top\":5, \"mode\":\"walk\"}` — \ +streams top-K predictions one at a time.\n\n\ +**Server → client** (text frames, JSON):\n\ +- `{\"type\":\"layer\", \"layer\":N, \"edges\":[...]}` — per-layer describe output.\n\ +- `{\"type\":\"done\", \"entity\":..., \"total_edges\":N, \"latency_ms\":M}` — describe finished.\n\ +- `{\"type\":\"prediction\", \"rank\":I, \"token\":..., \"probability\":P}` — inference result.\n\ +- `{\"type\":\"infer_done\", \"prompt\":..., \"mode\":..., \"predictions\":N, \"latency_ms\":M}` — inference finished.\n\ +- `{\"type\":\"error\", \"message\":...}` — protocol or runtime error.\n"), + ), +)] pub async fn handle_stream(State(state): State>, ws: WebSocketUpgrade) -> Response { ws.on_upgrade(move |socket| handle_socket(socket, state)) } diff --git a/crates/larql-server/src/routes/topology.rs b/crates/larql-server/src/routes/topology.rs index ae4e8f52d..5d2d045c2 100644 --- a/crates/larql-server/src/routes/topology.rs +++ b/crates/larql-server/src/routes/topology.rs @@ -14,10 +14,11 @@ use axum::extract::State; use axum::http::StatusCode; use axum::Json; use serde::Serialize; +use utoipa::ToSchema; use crate::state::AppState; -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct TopologyResponse { /// Model identifier (e.g. `"google/gemma-4-26B-A4B-it"`). pub model_id: String, @@ -31,6 +32,15 @@ pub struct TopologyResponse { pub owned_end: usize, } +#[utoipa::path( + get, + path = "/v1/expert/topology", + tag = "browse", + responses( + (status = 200, description = "Expert ownership range for this shard", body = TopologyResponse), + (status = 404, description = "Server was not launched with --experts"), + ), +)] pub async fn handle_topology( State(state): State>, ) -> Result, StatusCode> { diff --git a/crates/larql-server/src/routes/walk.rs b/crates/larql-server/src/routes/walk.rs index 5ade4f2fb..31e5ba4a8 100644 --- a/crates/larql-server/src/routes/walk.rs +++ b/crates/larql-server/src/routes/walk.rs @@ -9,11 +9,15 @@ use serde::Deserialize; use crate::error::ServerError; use crate::state::{elapsed_ms, AppState, LoadedModel}; -#[derive(Deserialize)] +#[derive(Deserialize, utoipa::IntoParams)] +#[into_params(parameter_in = Query)] pub struct WalkParams { + /// Prompt text to scan for features. pub prompt: String, + /// Top-K features per layer. #[serde(default = "default_top")] pub top: usize, + /// Restrict scan to these layers — either a range (`"24-33"`) or a comma list (`"14,26,27"`). #[serde(default)] pub layers: Option, } @@ -88,6 +92,17 @@ fn walk_prompt(model: &LoadedModel, params: &WalkParams) -> Result>, Query(params): Query, @@ -100,6 +115,21 @@ pub async fn handle_walk( Ok(Json(result)) } +#[utoipa::path( + get, + path = "/v1/{model_id}/walk", + tag = "browse", + params( + ("model_id" = String, Path, description = "Id of a loaded vindex."), + WalkParams, + ), + responses( + (status = 200, body = crate::openapi::schemas::WalkResponse), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_walk_multi( State(state): State>, Path(model_id): Path, diff --git a/crates/larql-server/src/routes/walk_ffn.rs b/crates/larql-server/src/routes/walk_ffn.rs index 0e7ed79f6..e31451634 100644 --- a/crates/larql-server/src/routes/walk_ffn.rs +++ b/crates/larql-server/src/routes/walk_ffn.rs @@ -684,6 +684,25 @@ fn run_walk_ffn(state: &AppState, req: &WalkFfnRequest) -> Result), + (status = 200, description = "Binary packed output when the request was `application/x-larql-ffn`", + content_type = "application/x-larql-ffn", body = Vec), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_walk_ffn( State(state): State>, request: axum::extract::Request, @@ -774,6 +793,22 @@ pub(crate) const Q8K_BATCH_CT: &str = "application/x-larql-ffn-q8k-batch"; /// /// Returns 404 if the vindex doesn't have interleaved Q4K data (ffn-only /// servers without Q4K weights can't serve this endpoint). +#[utoipa::path( + post, + path = "/v1/walk-ffn-q8k", + tag = "expert", + request_body( + content_type = "application/x-larql-ffn-q8k-batch", + description = "Q8K-prenormed dense-FFN batch: client has applied FFN input norm + Q8 quantisation. \ + 404 if the vindex lacks interleaved Q4K data.", + ), + responses( + (status = 200, content_type = "application/x-larql-ffn-q8k-batch", + description = "Per-layer FFN delta as f32", body = Vec), + (status = 400, body = crate::error::ErrorBody), + (status = 404, body = crate::error::ErrorBody), + ), +)] pub async fn handle_walk_ffn_q8k( State(state): State>, request: axum::extract::Request, diff --git a/crates/larql-server/src/routes/warmup.rs b/crates/larql-server/src/routes/warmup.rs index a575ea02e..1c4db6cbc 100644 --- a/crates/larql-server/src/routes/warmup.rs +++ b/crates/larql-server/src/routes/warmup.rs @@ -26,7 +26,7 @@ use tracing::info; use crate::error::ServerError; use crate::state::{AppState, LoadedModel}; -#[derive(Default, Deserialize)] +#[derive(Default, Deserialize, utoipa::ToSchema)] pub struct WarmupRequest { /// Specific layers to prefetch (`madvise WILLNEED`). Defaults to /// every owned layer when omitted — the typical case for boot @@ -48,7 +48,7 @@ pub struct WarmupRequest { pub warmup_hnsw: bool, } -#[derive(Serialize)] +#[derive(Serialize, utoipa::ToSchema)] pub struct WarmupResponse { pub model: String, pub weights_loaded: bool, @@ -164,6 +164,16 @@ pub async fn warmup_model_async(model: Arc, req: WarmupRequest) -> .expect("warmup spawn_blocking") } +#[utoipa::path( + post, + path = "/v1/warmup", + tag = "inference", + request_body = WarmupRequest, + responses( + (status = 200, description = "Warmup completed", body = WarmupResponse), + (status = 500, body = crate::error::ErrorBody), + ), +)] pub async fn handle_warmup( State(state): State>, body: Option>,