diff --git a/Cargo.lock b/Cargo.lock index e2fd29b..0ce3965 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,3913 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aha" version = "0.0.0" +dependencies = [ + "anyhow", + "base64 0.22.1", + "candle-core", + "candle-flash-attn", + "candle-nn", + "candle-transformers", + "ffmpeg-next", + "image", + "minijinja", + "num", + "openai_dive", + "reqwest", + "serde", + "serde_json", + "tokenizers", +] + +[[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.3", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +dependencies = [ + "backtrace", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[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 = "av1-grain" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[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 = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.9.4", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bindgen_cuda" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8489af5b7d17a81bffe37e0f4d6e1e4de87c87329d05447f22c35d95a1227d" +dependencies = [ + "glob", + "num_cpus", + "rayon", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytemuck" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "candle-core" +version = "0.9.1" +source = "git+https://github.com/huggingface/candle.git#1a699fb2018d316dd5f6c1c179588e4cfcd7888e" +dependencies = [ + "byteorder", + "candle-kernels", + "cudarc", + "float8 0.4.2", + "gemm 0.17.1", + "half", + "memmap2", + "num-traits", + "num_cpus", + "rand 0.9.2", + "rand_distr", + "rayon", + "safetensors", + "thiserror 1.0.69", + "ug", + "ug-cuda", + "yoke 0.7.5", + "zip", +] + +[[package]] +name = "candle-flash-attn" +version = "0.9.1" +source = "git+https://github.com/huggingface/candle.git#1a699fb2018d316dd5f6c1c179588e4cfcd7888e" +dependencies = [ + "anyhow", + "bindgen_cuda", + "candle-core", + "half", +] + +[[package]] +name = "candle-kernels" +version = "0.9.1" +source = "git+https://github.com/huggingface/candle.git#1a699fb2018d316dd5f6c1c179588e4cfcd7888e" +dependencies = [ + "bindgen_cuda", +] + +[[package]] +name = "candle-nn" +version = "0.9.1" +source = "git+https://github.com/huggingface/candle.git#1a699fb2018d316dd5f6c1c179588e4cfcd7888e" +dependencies = [ + "candle-core", + "half", + "libc", + "num-traits", + "rayon", + "safetensors", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "candle-transformers" +version = "0.9.1" +source = "git+https://github.com/huggingface/candle.git#1a699fb2018d316dd5f6c1c179588e4cfcd7888e" +dependencies = [ + "byteorder", + "candle-core", + "candle-nn", + "fancy-regex", + "num-traits", + "rand 0.9.2", + "rayon", + "serde", + "serde_json", + "serde_plain", + "tracing", +] + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[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 = "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-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[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 = "cudarc" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ba848ae5c6f3cb36e71eab5f268763e3fabcabe3f7bc683e16f7fa3d46281e" +dependencies = [ + "float8 0.3.0", + "half", + "libloading", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d2e3287df1c007e74221c49ca10a95d557349e54b3a75dc2fb14712c751f04" +dependencies = [ + "serde", +] + +[[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 = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-stack" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e53799688f5632f364f8fb387488dd05db9fe45db7011be066fc20e7027f8b" +dependencies = [ + "bytemuck", + "reborrow", +] + +[[package]] +name = "dyn-stack" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490bd48eb68fffcfed519b4edbfd82c69cbe741d175b84f0e0cbe8c57cbe0bdd" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[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 = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.0", +] + +[[package]] +name = "esaxx-rs" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6" +dependencies = [ + "cc", +] + +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fancy-regex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ffmpeg-next" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d658424d233cbd993a972dd73a66ca733acd12a494c68995c9ac32ae1fe65b40" +dependencies = [ + "bitflags 2.9.4", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bca20aa4ee774fe384c2490096c122b0b23cf524a9910add0686691003d797b" +dependencies = [ + "bindgen", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float8" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f498aec3b227cd892ce18967f4033d9d397d28a80a7ab67e9f6b0176a79654e" +dependencies = [ + "half", +] + +[[package]] +name = "float8" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4203231de188ebbdfb85c11f3c20ca2b063945710de04e7b59268731e728b462" +dependencies = [ + "cudarc", + "half", + "num-traits", + "rand 0.9.2", + "rand_distr", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[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 = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gemm" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab24cc62135b40090e31a76a9b2766a501979f3070fa27f689c27ec04377d32" +dependencies = [ + "dyn-stack 0.10.0", + "gemm-c32 0.17.1", + "gemm-c64 0.17.1", + "gemm-common 0.17.1", + "gemm-f16 0.17.1", + "gemm-f32 0.17.1", + "gemm-f64 0.17.1", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab96b703d31950f1aeddded248bc95543c9efc7ac9c4a21fda8703a83ee35451" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-c32 0.18.2", + "gemm-c64 0.18.2", + "gemm-common 0.18.2", + "gemm-f16 0.18.2", + "gemm-f32 0.18.2", + "gemm-f64 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.6.0", + "seq-macro", +] + +[[package]] +name = "gemm-c32" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c030d0b983d1e34a546b86e08f600c11696fde16199f971cd46c12e67512c0" +dependencies = [ + "dyn-stack 0.10.0", + "gemm-common 0.17.1", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-c32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6db9fd9f40421d00eea9dd0770045a5603b8d684654816637732463f4073847" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.6.0", + "seq-macro", +] + +[[package]] +name = "gemm-c64" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb5f2e79fefb9693d18e1066a557b4546cd334b226beadc68b11a8f9431852a" +dependencies = [ + "dyn-stack 0.10.0", + "gemm-common 0.17.1", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-c64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcad8a3d35a43758330b635d02edad980c1e143dc2f21e6fd25f9e4eada8edf" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.6.0", + "seq-macro", +] + +[[package]] +name = "gemm-common" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2e7ea062c987abcd8db95db917b4ffb4ecdfd0668471d8dc54734fdff2354e8" +dependencies = [ + "bytemuck", + "dyn-stack 0.10.0", + "half", + "num-complex", + "num-traits", + "once_cell", + "paste", + "pulp 0.18.22", + "raw-cpuid 10.7.0", + "rayon", + "seq-macro", + "sysctl 0.5.5", +] + +[[package]] +name = "gemm-common" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a352d4a69cbe938b9e2a9cb7a3a63b7e72f9349174a2752a558a8a563510d0f3" +dependencies = [ + "bytemuck", + "dyn-stack 0.13.0", + "half", + "libm", + "num-complex", + "num-traits", + "once_cell", + "paste", + "pulp 0.21.5", + "raw-cpuid 11.6.0", + "rayon", + "seq-macro", + "sysctl 0.6.0", +] + +[[package]] +name = "gemm-f16" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca4c06b9b11952071d317604acb332e924e817bd891bec8dfb494168c7cedd4" +dependencies = [ + "dyn-stack 0.10.0", + "gemm-common 0.17.1", + "gemm-f32 0.17.1", + "half", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 10.7.0", + "rayon", + "seq-macro", +] + +[[package]] +name = "gemm-f16" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff95ae3259432f3c3410eaa919033cd03791d81cebd18018393dc147952e109" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "gemm-f32 0.18.2", + "half", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.6.0", + "rayon", + "seq-macro", +] + +[[package]] +name = "gemm-f32" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a69f51aaefbd9cf12d18faf273d3e982d9d711f60775645ed5c8047b4ae113" +dependencies = [ + "dyn-stack 0.10.0", + "gemm-common 0.17.1", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-f32" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc8d3d4385393304f407392f754cd2dc4b315d05063f62cf09f47b58de276864" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.6.0", + "seq-macro", +] + +[[package]] +name = "gemm-f64" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa397a48544fadf0b81ec8741e5c0fba0043008113f71f2034def1935645d2b0" +dependencies = [ + "dyn-stack 0.10.0", + "gemm-common 0.17.1", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 10.7.0", + "seq-macro", +] + +[[package]] +name = "gemm-f64" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b2a4f76ce4b8b16eadc11ccf2e083252d8237c1b589558a49b0183545015bd" +dependencies = [ + "dyn-stack 0.13.0", + "gemm-common 0.18.2", + "num-complex", + "num-traits", + "paste", + "raw-cpuid 11.6.0", + "seq-macro", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "bytemuck", + "cfg-if", + "crunchy", + "num-traits", + "rand 0.9.2", + "rand_distr", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[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 = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "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 = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke 0.8.0", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke 0.8.0", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[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 = "image" +version = "0.25.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[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.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +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.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[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 = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memmap2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +dependencies = [ + "libc", + "stable_deref_trait", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minijinja" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9f264d75233323f4b7d2f03aefe8a990690cdebfbfe26ea86bcbaec5e9ac990" +dependencies = [ + "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.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "monostate" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f766eeb5719df144c29802e56f3f7e0b3f29bd3ec8ab6c819aa1eaddec3f80c" +dependencies = [ + "monostate-impl", + "serde", + "serde_core", +] + +[[package]] +name = "monostate-impl" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d4b3dcd6ce9277a52fb05f00bf3e45d475af8cce7376de2f8d6bd065fa4adb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "moxcms" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[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 = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "bytemuck", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "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 = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "onig" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" +dependencies = [ + "bitflags 2.9.4", + "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 = "openai_dive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac82ff995cdbe4e120fa87fe4d76bd452866dd7b0c9bcbdb562ba4be0f1786d" +dependencies = [ + "bytes", + "derive_builder", + "reqwest", + "serde", + "serde_html_form", + "serde_json", + "tokio", + "tokio-util", +] + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "foreign-types", + "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.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +dependencies = [ + "bitflags 2.9.4", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.6", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "pulp" +version = "0.18.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a01a0dc67cf4558d279f0c25b0962bd08fc6dec0137699eae304103e882fe6" +dependencies = [ + "bytemuck", + "libm", + "num-complex", + "reborrow", +] + +[[package]] +name = "pulp" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b86df24f0a7ddd5e4b95c94fc9ed8a98f1ca94d3b01bdce2824097e7835907" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "reborrow", + "version_check", +] + +[[package]] +name = "pxfm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f9b339b02259ada5c0f4a389b7fb472f933aa17ce176fd2ad98f28bb401fde" +dependencies = [ + "num-traits", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +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 = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[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.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "rand_distr" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" +dependencies = [ + "num-traits", + "rand 0.9.2", +] + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.9.4", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +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 = "reborrow" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +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", + "mime_guess", + "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 = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" + +[[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.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.9.4", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.0", +] + +[[package]] +name = "rustls" +version = "0.23.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +dependencies = [ + "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 = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "safetensors" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44560c11236a6130a46ce36c836a62936dc81ebf8c36a37947423571be0e55b6" +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.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.4", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + +[[package]] +name = "serde" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_html_form" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" +dependencies = [ + "form_urlencoded", + "indexmap", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[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 = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[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 = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[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.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +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 = "sysctl" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7dddc5f0fee506baf8b9fdb989e242f17e4b11c61dfbb0635b705217199eea" +dependencies = [ + "bitflags 2.9.4", + "byteorder", + "enum-as-inner", + "libc", + "thiserror 1.0.69", + "walkdir", +] + +[[package]] +name = "sysctl" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc" +dependencies = [ + "bitflags 2.9.4", + "byteorder", + "enum-as-inner", + "libc", + "thiserror 1.0.69", + "walkdir", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.9.4", + "core-foundation", + "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-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tempfile" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.61.0", +] + +[[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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", +] + +[[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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiff" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokenizers" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6475a27088c98ea96d00b39a9ddfb63780d1ad4cceb6f48374349a96ab2b7842" +dependencies = [ + "ahash", + "aho-corasick", + "compact_str", + "dary_heap", + "derive_builder", + "esaxx-rs", + "getrandom 0.3.3", + "indicatif", + "itertools 0.14.0", + "log", + "macro_rules_attribute", + "monostate", + "onig", + "paste", + "rand 0.9.2", + "rayon", + "rayon-cond", + "regex", + "regex-syntax", + "serde", + "serde_json", + "spm_precompiled", + "thiserror 2.0.16", + "unicode-normalization-alignments", + "unicode-segmentation", + "unicode_categories", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "socket2", + "windows-sys 0.59.0", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +dependencies = [ + "indexmap", + "toml_datetime 0.7.2", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +dependencies = [ + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.4", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[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.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "ug" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b761acf8af3494640d826a8609e2265e19778fb43306c7f15379c78c9b05b0" +dependencies = [ + "gemm 0.18.2", + "half", + "libloading", + "memmap2", + "num", + "num-traits", + "num_cpus", + "rayon", + "safetensors", + "serde", + "thiserror 1.0.69", + "tracing", + "yoke 0.7.5", +] + +[[package]] +name = "ug-cuda" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0a1fa748f26166778c33b8498255ebb7c6bffb472bcc0a72839e07ebb1d9b5" +dependencies = [ + "cudarc", + "half", + "serde", + "thiserror 1.0.69", + "ug", +] + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[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.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[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 = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[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 = "web-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +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 = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + +[[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.0", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link 0.1.3", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[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_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[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_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[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_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[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_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[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_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.7.5", + "zerofrom", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.8.0", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke 0.8.0", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke 0.8.0", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "indexmap", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 4f7c6c7..bc67317 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,21 @@ license = "Apache-2.0" description = "aha" [dependencies] +candle-core = { git = "https://github.com/huggingface/candle.git", version = "0.9.1"} +candle-nn = { git = "https://github.com/huggingface/candle.git", version = "0.9.1"} +candle-transformers = { git = "https://github.com/huggingface/candle.git", version = "0.9.1"} +candle-flash-attn = { git = "https://github.com/huggingface/candle.git", optional = true } +serde = "1.0.226" +serde_json = "1.0.145" +anyhow = "1.0.100" +ffmpeg-next = "8.0.0" +image = "0.25.8" +reqwest = { version = "0.12.23", features = ["blocking"] } +base64 = "0.22.1" +num = "0.4.3" +minijinja = "2.12.0" +tokenizers = "0.22.1" +openai_dive = "1.3.0" +[features] +flash-attn=["candle-flash-attn"] +cuda=["candle-nn/cuda", "candle-core/cuda", "candle-transformers/cuda"] diff --git a/assets/img/ocr_test.png b/assets/img/ocr_test.png new file mode 100644 index 0000000..b69f68e Binary files /dev/null and b/assets/img/ocr_test.png differ diff --git a/assets/img/ocr_test1.png b/assets/img/ocr_test1.png new file mode 100644 index 0000000..39ab8dd Binary files /dev/null and b/assets/img/ocr_test1.png differ diff --git a/src/chat_template/chat_template.rs b/src/chat_template/chat_template.rs new file mode 100644 index 0000000..b290195 --- /dev/null +++ b/src/chat_template/chat_template.rs @@ -0,0 +1,104 @@ +use crate::utils::utils::string_to_static_str; +use anyhow::{Result, anyhow}; +use minijinja::{Environment, Value as MiniJinjaValue, context}; +use openai_dive::v1::resources::chat::ChatCompletionParameters; + +pub fn get_template(path: String) -> Result { + let tokenizer_config_file = path.clone() + "/tokenizer_config.json"; + assert!( + std::path::Path::new(&tokenizer_config_file).exists(), + "tokenizer_config.json not exists in model path" + ); + let tokenizer_config: serde_json::Value = + serde_json::from_slice(&std::fs::read(tokenizer_config_file)?) + .map_err(|e| anyhow!(format!("load tokenizer_config file error:{}", e)))?; + let chat_template = tokenizer_config["chat_template"] + .as_str() + .ok_or(anyhow!(format!("chat_template to str error")))?; + // 修复模板中的问题行 + let fixed_template = chat_template + .replace( + "message.content.startswith('')", + "message.content is startingwith('')", // 使用minijinja中的 is startingwith 替换 + ) + .replace( + "message.content.endswith('')", + "message.content is endingwith('')", // 使用minijinja中的 is endingwith 替换 + ) + .replace( + "content.split('')[0].rstrip('\\n').split('')[-1].lstrip('\\n')", + "((content | split(''))[0] | rstrip('\\n') | split(''))[-1] | lstrip('\\n')", // 使用自定义的split, rstrip, lstrip过滤器替换 + ) + .replace( + "content.split('')[-1].lstrip('\\n')", + "(content | split(''))[-1] | lstrip('\\n')", // 使用自定义的过滤器替换 + ) + .replace( + "reasoning_content.strip('\\n')", + "reasoning_content | strip('\\n')", // 使用自定义的过滤器替换 + ) + .replace( + "content.lstrip('\\n')", + "content | lstrip('\\n')", // 使用自定义的过滤器替换 + ); + Ok(fixed_template) +} + +pub struct ChatTemplate<'a> { + env: Environment<'a>, +} + +impl<'a> ChatTemplate<'a> { + pub fn init(path: &str) -> Result { + let path = path.to_string(); + assert!( + std::path::Path::new(&path).exists(), + "model path file not exists" + ); + let template = get_template(path)?; + let template = string_to_static_str(template); + // 加载jinjaenv处理chat_template + let mut env = Environment::new(); + // 添加自定义过滤器 + env.add_filter("tojson", |v: MiniJinjaValue| { + serde_json::to_string(&v).unwrap() + }); + + env.add_filter("split", |s: String, delimiter: String| { + s.split(&delimiter) + .map(|s| s.to_string()) + .collect::>() + }); + + // 添加 lstrip 过滤器 + env.add_filter("lstrip", |s: String, chars: Option| match chars { + Some(chars_str) => s.trim_start_matches(chars_str.as_str()).to_string(), + None => s.trim_start().to_string(), + }); + + // 添加 rstrip 过滤器 + env.add_filter("rstrip", |s: String, chars: Option| match chars { + Some(chars_str) => s.trim_end_matches(chars_str.as_str()).to_string(), + None => s.trim_end().to_string(), + }); + // let template = get_template(path.to_string())?; + let _ = env.add_template("chat", template); + + Ok(Self { env }) + } + + pub fn apply_chat_template(&self, messages: &ChatCompletionParameters) -> Result { + let context = context! { + messages => &messages.messages, + add_generation_prompt => true, + }; + let template = self + .env + .get_template("chat") + .map_err(|e| anyhow!(format!("render template error {}", e)))?; + let message_str = template + .render(context) + .map_err(|e| anyhow!(format!("render template error {}", e)))?; + Ok(message_str) + } +} diff --git a/src/chat_template/mod.rs b/src/chat_template/mod.rs new file mode 100644 index 0000000..6854abd --- /dev/null +++ b/src/chat_template/mod.rs @@ -0,0 +1 @@ +pub mod chat_template; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e69de29..897c0a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1,28 @@ +use crate::models::{GenerateModel, qwen2_5vl::generate::Qwen2_5VLGenerateModel}; +use anyhow::{Ok, Result}; +use candle_core::{DType, Device}; +pub mod chat_template; +pub mod models; +pub mod position_embed; +pub mod tokenizer; +pub mod utils; + +pub enum ModelType { + Qwen2_5VL, +} + +impl ModelType { + pub fn init( + model_type: ModelType, + model_path: &str, + device: Option<&Device>, + dtype: Option, + ) -> Result> { + match model_type { + ModelType::Qwen2_5VL => { + let model = Qwen2_5VLGenerateModel::init(model_path, device, dtype)?; + Ok(Box::new(model)) + } + } + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..10140c7 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,11 @@ +pub mod qwen2_5vl; +use anyhow::Result; +use candle_core::{DType, Device}; +use openai_dive::v1::resources::chat::ChatCompletionParameters; + +pub trait GenerateModel { + fn init(path: &str, device: Option<&Device>, dtype: Option) -> Result + where + Self: Sized; + fn generate(&mut self, mes: ChatCompletionParameters) -> Result; +} diff --git a/src/models/qwen2_5vl/config.rs b/src/models/qwen2_5vl/config.rs new file mode 100644 index 0000000..a3f3a9d --- /dev/null +++ b/src/models/qwen2_5vl/config.rs @@ -0,0 +1,97 @@ +use candle_nn::Activation; + +#[derive(Debug, Clone, PartialEq, serde::Deserialize)] +pub struct VisionConfig { + pub depth: usize, + pub hidden_act: Activation, + pub hidden_size: usize, + pub intermediate_size: usize, + pub num_heads: usize, + pub in_chans: usize, + pub out_hidden_size: usize, + pub patch_size: usize, + pub spatial_merge_size: usize, + pub spatial_patch_size: usize, + pub window_size: usize, + pub fullatt_block_indexes: Vec, + pub tokens_per_second: usize, + pub temporal_patch_size: usize, +} +#[derive(Debug, Clone, PartialEq, serde::Deserialize)] +pub struct RopeScaling { + pub r#type: String, + pub mrope_section: Vec, +} + +#[derive(Debug, Clone, PartialEq, serde::Deserialize)] +pub struct Config { + pub attention_dropout: f32, + pub bos_token_id: usize, + pub eos_token_id: usize, + pub vision_start_token_id: usize, + pub vision_end_token_id: usize, + pub vision_token_id: usize, + pub image_token_id: usize, + pub video_token_id: usize, + pub hidden_act: Activation, + pub hidden_size: usize, + pub initializer_range: f32, + pub intermediate_size: usize, + pub max_position_embeddings: usize, + pub max_window_layers: usize, + pub num_attention_heads: usize, + pub num_hidden_layers: usize, + pub num_key_value_heads: usize, + pub rms_norm_eps: f64, + pub rope_theta: f32, + pub sliding_window: usize, + pub tie_word_embeddings: bool, + pub torch_dtype: String, + pub use_sliding_window: bool, + pub vision_config: VisionConfig, + pub rope_scaling: RopeScaling, + pub vocab_size: usize, +} + +pub struct VisionSetting { + pub image_factor: u32, + pub min_pixels: u32, + pub max_pixels: u32, + pub max_ratio: u32, + pub temporal_patch_size: usize, + pub patch_size: usize, + pub merge_size: usize, + pub video_min_pixels: u32, + pub video_max_pixels: u32, + pub video_total_pixels: u32, + pub frame_factor: u32, + pub fps: f32, + pub fps_min_frames: u32, + pub fps_max_frames: u32, + pub image_mean: Vec, + pub image_std: Vec, +} + +impl VisionSetting { + pub fn default() -> Self { + Self { + image_factor: 28, + min_pixels: 4 * 28 * 28, + max_pixels: 16384 * 28 * 28, + // max_pixels: 1000 * 28 * 28, + max_ratio: 200, + temporal_patch_size: 2, + patch_size: 14, + merge_size: 2, + video_min_pixels: 128 * 28 * 28, + video_max_pixels: 768 * 28 * 28, + video_total_pixels: 24576 * 28 * 28, + frame_factor: 2, + fps: 2.0, + fps_min_frames: 4, + fps_max_frames: 768, + image_mean: vec![0.48145466_f32, 0.4578275, 0.40821073], + image_std: vec![0.26862954, 0.26130258, 0.27577711], + } + } +} diff --git a/src/models/qwen2_5vl/generate.rs b/src/models/qwen2_5vl/generate.rs new file mode 100644 index 0000000..6006834 --- /dev/null +++ b/src/models/qwen2_5vl/generate.rs @@ -0,0 +1,137 @@ +use crate::models::qwen2_5vl::config::Config; +use crate::utils::utils::{find_safetensors_files, get_device, get_dtype}; +use crate::{ + chat_template::chat_template::ChatTemplate, + models::{ + GenerateModel, + qwen2_5vl::{model::Qwen2_5VLModel, processor::Qwen2_5VLProcessor}, + }, + tokenizer::tokenizer::TokenizerModel, +}; +use anyhow::{Result, anyhow}; +use candle_core::{D, DType, Device, IndexOp, Tensor}; +use candle_nn::VarBuilder; +use candle_transformers::generation::LogitsProcessor; +use openai_dive::v1::resources::chat::ChatCompletionParameters; + +pub struct Qwen2_5VLGenerateModel<'a> { + chat_template: ChatTemplate<'a>, + tokenizer: TokenizerModel, + pre_processor: Qwen2_5VLProcessor, + qwen2_5_vl: Qwen2_5VLModel, + device: Device, + dtype: DType, +} + +impl<'a> GenerateModel for Qwen2_5VLGenerateModel<'a> { + fn init(path: &str, device: Option<&Device>, dtype: Option) -> Result { + let chat_template = ChatTemplate::init(path)?; + let tokenizer = TokenizerModel::init(path)?; + let config_path = path.to_string() + "/config.json"; + let cfg: Config = serde_json::from_slice(&std::fs::read(config_path)?)?; + let device = &get_device(device); + let cfg_dtype = cfg.torch_dtype.as_str(); + let dtype = get_dtype(dtype, cfg_dtype); + let pre_processor = Qwen2_5VLProcessor::new(device, dtype)?; + + let model_list = find_safetensors_files(&path)?; + let vb = unsafe { VarBuilder::from_mmaped_safetensors(&model_list, dtype, device)? }; + let qwen2_5_vl = Qwen2_5VLModel::new(cfg, vb)?; + Ok(Qwen2_5VLGenerateModel { + chat_template, + tokenizer, + pre_processor, + qwen2_5_vl, + device: device.clone(), + dtype: dtype, + }) + } + fn generate(&mut self, mes: ChatCompletionParameters) -> Result { + let temperature = match mes.temperature { + Some(temp) => Some(temp as f64), + None => None, + }; + let top_p = match mes.top_p { + Some(tp) => Some(tp as f64), + None => None, + }; + let mut logit_processor = LogitsProcessor::new(34562, temperature, top_p); + let mes_render = self.chat_template.apply_chat_template(&mes)?; + let input = self.pre_processor.process_info(&mes, &mes_render)?; + let mut input_ids = self + .tokenizer + .text_encode(input.replace_text.clone(), &self.device)?; + let mut seq_len = input_ids.dim(1)?; + let mut seqlen_offset = 0; + let end_of_text_id = self.qwen2_5_vl.cfg.bos_token_id as u32; + let im_end_id = self.qwen2_5_vl.cfg.eos_token_id as u32; + let mut pixel_values = if input.pixel_values.is_some() { + Some(&input.pixel_values.unwrap().clone()) + } else { + None + }; + let image_grid_thw = if input.image_grid_thw.is_some() { + Some(&input.image_grid_thw.unwrap().clone()) + } else { + None + }; + let mut pixel_values_video = if input.pixel_values_video.is_some() { + Some(&input.pixel_values_video.unwrap().clone()) + } else { + None + }; + let video_grid_thw = if input.video_grid_thw.is_some() { + Some(&input.video_grid_thw.unwrap().clone()) + } else { + None + }; + let second_per_grid_ts = if input.second_per_grid_ts.is_some() { + Some(input.second_per_grid_ts.unwrap().clone()) + } else { + None + }; + + let mut mask = Tensor::ones_like(&input_ids)?; + let mut cache_position = Tensor::ones_like(&input_ids.i(0)?)? + .to_dtype(candle_core::DType::F64)? + .cumsum(D::Minus1)? + .to_dtype(candle_core::DType::U32)? + .broadcast_sub(&Tensor::new(vec![1_u32], input_ids.device())?)?; + + let mut generate = Vec::new(); + let sample_len = match mes.max_tokens { + Some(max) => max, + None => 512, + }; + for _ in 0..sample_len { + let logits = self.qwen2_5_vl.forward( + &input_ids, + pixel_values, + image_grid_thw, + pixel_values_video, + video_grid_thw, + &mask, + Some(&cache_position), + seqlen_offset, + second_per_grid_ts.clone(), + )?; + let logits = logits.squeeze(0)?.squeeze(0)?.to_dtype(DType::F32)?; + let next_token = logit_processor.sample(&logits)?; + generate.push(next_token); + if next_token == end_of_text_id || next_token == im_end_id { + break; + } + seqlen_offset += seq_len; + seq_len = 1; + input_ids = Tensor::from_vec(vec![next_token], (1, 1), &self.device)?; + let appendd_mask = Tensor::ones((1, 1), mask.dtype(), &self.device)?; + mask = Tensor::cat(&[mask, appendd_mask], 1)?; + cache_position = Tensor::from_vec(vec![seqlen_offset as u32], 1, &self.device)?; + pixel_values = None; + pixel_values_video = None; + } + let res = self.tokenizer.token_decode(generate)?; + self.qwen2_5_vl.clear_kv_cache(); + Ok(res) + } +} diff --git a/src/models/qwen2_5vl/mod.rs b/src/models/qwen2_5vl/mod.rs new file mode 100644 index 0000000..8d2989c --- /dev/null +++ b/src/models/qwen2_5vl/mod.rs @@ -0,0 +1,4 @@ +pub mod config; +pub mod model; +pub mod processor; +pub mod generate; \ No newline at end of file diff --git a/src/models/qwen2_5vl/model.rs b/src/models/qwen2_5vl/model.rs new file mode 100644 index 0000000..0a32f00 --- /dev/null +++ b/src/models/qwen2_5vl/model.rs @@ -0,0 +1,1229 @@ +use crate::{ + models::qwen2_5vl::config::{Config, RopeScaling}, + position_embed::rope::{ + Qwen2_5VLTextRotaryEmbedding, Qwen2_5VisionRotaryEmbedding, apply_rotary_pos_emb, + apply_rotary_pos_emb_vision, + }, + utils::tensor_utils::{ + get_equal_mask, get_vision_next_indices, masked_scatter_dim0, nonzero_index, + safe_arg_sort_last_dim, zero_index, + }, +}; +use anyhow::{Result, anyhow}; +use candle_core::{D, DType, Device, IndexOp, Tensor}; +use candle_nn::{ + Activation, Init, Linear, Module, RmsNorm, VarBuilder, linear, linear_no_bias, rms_norm, +}; + +pub struct Qwen2_5VisionPatchEmbed { + conv3d_weight: Tensor, +} + +impl Qwen2_5VisionPatchEmbed { + pub fn new(cfg: &Config, vb: VarBuilder) -> Result { + let patch_size = cfg.vision_config.patch_size; + let temporal_patch_size = cfg.vision_config.temporal_patch_size; + let in_channels = cfg.vision_config.in_chans; + let embed_dim = cfg.vision_config.hidden_size; + // conv3d weight key: visual.patch_embed.proj.weight, value: Tensor[dims 1280, 3, 2, 14, 14; bf16, cuda:0] + // (1280, 3, 2, 14, 14) -> (1280, 1176) -> (1176, 1280) + let conv3d_weight = vb + .get_with_hints( + ( + embed_dim, + in_channels, + temporal_patch_size, + patch_size, + patch_size, + ), + "proj.weight", + Init::Const(1.), + )? + .flatten(1, 4)? + .t()?; + Ok(Self { conv3d_weight }) + } + + pub fn forward(&self, hidden_states: &Tensor) -> Result { + // hidden_states shape: (grid_t*grid_h*grid_w, c*temporal_patch_size*patch_size*patch_size) + // ((), 1176) matmul (1176, 1280) -> ((), 1280) + let hidden_states = hidden_states.matmul(&self.conv3d_weight)?; + Ok(hidden_states) + } +} + +pub struct Qwen2_5VLPatchMerger { + hidden_size: usize, + ln_q: RmsNorm, + mlp_0: Linear, + mlp_2: Linear, +} + +impl Qwen2_5VLPatchMerger { + pub fn new(cfg: &Config, vb: VarBuilder) -> Result { + let hidden_size = + cfg.vision_config.hidden_size * (cfg.vision_config.spatial_merge_size.pow(2)); + let ln_q = rms_norm( + cfg.vision_config.hidden_size, + cfg.rms_norm_eps, + vb.pp("ln_q"), + )?; + let mlp_0 = linear(hidden_size, hidden_size, vb.pp("mlp.0"))?; + let mlp_2 = linear( + hidden_size, + cfg.vision_config.out_hidden_size, + vb.pp("mlp.2"), + )?; + Ok(Self { + hidden_size, + ln_q, + mlp_0, + mlp_2, + }) + } +} +impl Module for Qwen2_5VLPatchMerger { + fn forward(&self, xs: &Tensor) -> candle_core::Result { + let xs = xs.apply(&self.ln_q)?.reshape(((), self.hidden_size))?; + let xs = xs.apply(&self.mlp_0)?.gelu()?.apply(&self.mlp_2)?; + Ok(xs) + } +} + +#[derive(Debug, Clone)] +struct Qwen2_5VLVisionMLP { + gate_proj: Linear, + up_proj: Linear, + down_proj: Linear, + act_fn: Activation, +} + +impl Qwen2_5VLVisionMLP { + fn new(cfg: &Config, vb: VarBuilder) -> Result { + let hidden_sz = cfg.vision_config.hidden_size; + let intermediate_sz = cfg.vision_config.intermediate_size; + let gate_proj = linear(hidden_sz, intermediate_sz, vb.pp("gate_proj"))?; + let up_proj = linear(hidden_sz, intermediate_sz, vb.pp("up_proj"))?; + let down_proj = linear(intermediate_sz, hidden_sz, vb.pp("down_proj"))?; + Ok(Self { + gate_proj, + up_proj, + down_proj, + act_fn: cfg.hidden_act, + }) + } +} + +impl Module for Qwen2_5VLVisionMLP { + fn forward(&self, xs: &Tensor) -> candle_core::Result { + let lhs = xs.apply(&self.gate_proj)?.apply(&self.act_fn)?; + let rhs = xs.apply(&self.up_proj)?; + (lhs * rhs)?.apply(&self.down_proj) + } +} + +#[derive(Debug, Clone)] +struct Qwen2_5VLVisionAttention { + qkv: Linear, + proj: Linear, + num_heads: usize, + scale: Tensor, +} + +impl Qwen2_5VLVisionAttention { + fn new(cfg: &Config, vb: VarBuilder) -> Result { + let hidden_size = cfg.vision_config.hidden_size; + let num_heads = cfg.vision_config.num_heads; + let head_dim = hidden_size / num_heads; + let qkv = linear(hidden_size, hidden_size * 3, vb.pp("qkv"))?; + let proj = linear(hidden_size, hidden_size, vb.pp("proj"))?; + let scale = Tensor::new(vec![1f32 / (head_dim as f32).sqrt()], vb.device())? + .to_dtype(vb.dtype())?; + Ok(Self { + qkv, + proj, + num_heads, + scale, + }) + } + + fn forward( + &self, + xs: &Tensor, + cos: &Tensor, + sin: &Tensor, + attention_mask: &Tensor, + ) -> Result { + // xs: (seq_len, hidden_size) + let seq_length = xs.dim(0)?; + // (seq_len, hidden_size) -> (seq_len, hidden_size*3) + // -> (seq_len, 3, num_heads, head_dim) -> (3, seq_len, num_heads, head_dim) + let qkv_states = xs + .apply(&self.qkv)? + .reshape((seq_length, 3, self.num_heads, ()))? + .permute((1, 0, 2, 3))?; + // (seq_len, num_heads, head_dim) + let query_states = qkv_states.i(0)?.contiguous()?; + let key_states = qkv_states.i(1)?.contiguous()?; + let value_states = qkv_states.i(2)?.contiguous()?; + let (query_states, key_states) = + apply_rotary_pos_emb_vision(&query_states, &key_states, cos, sin)?; + // (seq_len, num_heads, head_dim) -> (num_heads, seq_len, head_dim) + let query_states = query_states.transpose(0, 1)?.contiguous()?; + let key_states = key_states.transpose(0, 1)?.contiguous()?; + let value_states = value_states.transpose(0, 1)?.contiguous()?; + + let attn_output = { + let attn_weights = query_states + .matmul(&key_states.transpose(D::Minus2, D::Minus1)?)? + .broadcast_mul(&self.scale)?; + let attn_weights = attn_weights.broadcast_add(&attention_mask)?; + let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?; + attn_weights.matmul(&value_states)? + }; + // (num_heads, seq_len, head_dim) -> (seq_len, num_heads, head_dim) -> (seq_len, hidden_size) + let attn_output = attn_output + .transpose(0, 1)? + .reshape((seq_length, ()))? + .contiguous()?; + let attn_ouput = attn_output.apply(&self.proj)?; + Ok(attn_ouput) + } +} + +#[derive(Debug, Clone)] +struct Qwen2_5VLVisionBlock { + attn: Qwen2_5VLVisionAttention, + mlp: Qwen2_5VLVisionMLP, + norm1: RmsNorm, + norm2: RmsNorm, +} + +impl Qwen2_5VLVisionBlock { + fn new(cfg: &Config, vb: VarBuilder) -> Result { + let attn = Qwen2_5VLVisionAttention::new(cfg, vb.pp("attn"))?; + let mlp = Qwen2_5VLVisionMLP::new(cfg, vb.pp("mlp"))?; + let norm1 = rms_norm( + cfg.vision_config.hidden_size, + cfg.rms_norm_eps, + vb.pp("norm1"), + )?; + let norm2 = rms_norm( + cfg.vision_config.hidden_size, + cfg.rms_norm_eps, + vb.pp("norm2"), + )?; + + Ok(Self { + attn, + mlp, + norm1, + norm2, + }) + } + + fn forward( + &self, + xs: &Tensor, + cos: &Tensor, + sin: &Tensor, + attention_mask: &Tensor, + ) -> Result { + let residual = xs; + let xs = self.norm1.forward(xs)?; + let xs = self.attn.forward(&xs, cos, sin, attention_mask)?; + let xs = (xs + residual)?; + let residual = &xs; + let xs = xs.apply(&self.norm2)?.apply(&self.mlp)?; + let xs = (residual + xs)?; + Ok(xs) + } +} + +pub struct Qwen2_5VLVisionModel { + spatial_merge_size: usize, + patch_size: usize, + fullatt_block_indexes: Vec, + window_size: usize, + spatial_merge_unit: usize, + patch_embed: Qwen2_5VisionPatchEmbed, + rotary_pos_emb: Qwen2_5VisionRotaryEmbedding, + blocks: Vec, + merger: Qwen2_5VLPatchMerger, + dtype: DType, +} + +impl Qwen2_5VLVisionModel { + pub fn new(cfg: &Config, vb: VarBuilder) -> Result { + let spatial_merge_size = cfg.vision_config.spatial_merge_size; + let patch_size = cfg.vision_config.patch_size; + let fullatt_block_indexes = cfg.vision_config.fullatt_block_indexes.clone(); + let window_size = cfg.vision_config.window_size; + let spatial_merge_unit = spatial_merge_size * spatial_merge_size; + let head_dim = cfg.vision_config.hidden_size / cfg.vision_config.num_heads; + let patch_embed = Qwen2_5VisionPatchEmbed::new(cfg, vb.pp("patch_embed"))?; + let rotary_pos_emb = Qwen2_5VisionRotaryEmbedding::new(head_dim / 2, None); + let mut blocks = Vec::new(); + let vb_blocks = vb.pp("blocks"); + for i in 0..cfg.vision_config.depth { + let block = Qwen2_5VLVisionBlock::new(cfg, vb_blocks.pp(i))?; + blocks.push(block); + } + let merger = Qwen2_5VLPatchMerger::new(cfg, vb.pp("merger"))?; + let dtype = vb.dtype(); + Ok(Self { + spatial_merge_size, + patch_size, + fullatt_block_indexes, + window_size, + spatial_merge_unit, + patch_embed, + rotary_pos_emb, + blocks, + merger, + dtype, + }) + } + + pub fn rot_pos_emb(&self, grid_thw: &Tensor) -> Result { + let mut pos_ids = Vec::new(); + for i in 0..grid_thw.dim(0)? { + let [t, h, w] = grid_thw.i(i)?.to_vec1::()?[..] else { + return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); + }; + // hpos_ids shape (h, w) + let hpos_ids = Tensor::arange(0, h, grid_thw.device())? + .unsqueeze(1)? + .expand((h as usize, w as usize))?; + let hpos_ids = hpos_ids.reshape(( + h as usize / self.spatial_merge_size, + self.spatial_merge_size, + w as usize / self.spatial_merge_size, + self.spatial_merge_size, + ))?; + let hpos_ids = hpos_ids.permute((0, 2, 1, 3))?.flatten_all()?; + let wpos_ids = Tensor::arange(0, w, grid_thw.device())? + .unsqueeze(0)? + .expand((h as usize, w as usize))?; + let wpos_ids = wpos_ids.reshape(( + h as usize / self.spatial_merge_size, + self.spatial_merge_size, + w as usize / self.spatial_merge_size, + self.spatial_merge_size, + ))?; + let wpos_ids = wpos_ids.permute((0, 2, 1, 3))?.flatten_all()?; + // thw_pos_ids shape (h*w, 2) + let thw_pos_ids = + Tensor::stack(&[&hpos_ids, &wpos_ids], D::Minus1)?.repeat((t as usize, 1))?; + pos_ids.push(thw_pos_ids); + } + let pos_ids = Tensor::cat(&pos_ids, 0)?.contiguous()?; + let max_grid_size = grid_thw.i((.., 1..))?.max_all()?.to_scalar::()?; + let rotary_pos_emb_full = self + .rotary_pos_emb + .forward(max_grid_size as usize, grid_thw.device())?; + + // contiguous()一定要加!!!很重要!!!!,不然index_select出来的是错的 + // 找错找了半天,都是泪啊,做维度索引操作后contiguous顺手写上总没错 + // 第一列是h维度的索引 + let pos_ids_h = pos_ids.i((.., 0))?.contiguous()?; + // 第二列是w维度的索引 + let pos_ids_w = pos_ids.i((.., 1))?.contiguous()?; + let rotary_pos_emb_h = rotary_pos_emb_full.index_select(&pos_ids_h, 0)?; + let rotary_pos_emb_w = rotary_pos_emb_full.index_select(&pos_ids_w, 0)?; + // 每个patch融合h索引和w索引两个的位置编码信息 + let rotary_pos_emb = Tensor::cat(&[rotary_pos_emb_h, rotary_pos_emb_w], 1)?.contiguous()?; + Ok(rotary_pos_emb) + } + + pub fn get_window_index(&self, grid_thw: &Tensor) -> Result<(Tensor, Tensor)> { + let mut window_index = Vec::new(); + let mut cu_window_seqlens = vec![0]; + let mut window_index_id = 0_i64; + + let vit_merger_window_size = + (self.window_size / self.spatial_merge_size / self.patch_size) as u32; + for i in 0..grid_thw.dim(0)? { + let [grid_t, grid_h, grid_w] = grid_thw.i(i)?.to_vec1::()?[..] else { + return Err(anyhow!(format!("grid_thw Expected exactly 3 elements"))); + }; + let llm_grid_h = grid_h / self.spatial_merge_size as u32; + let llm_grid_w = grid_w / self.spatial_merge_size as u32; + // 因为后续需要使用-100来做填充,所以需要int类型 + // candle好像不支持i32, DType里面都没有定义i32, 所以这里使用i64 + let mut index = Tensor::arange( + window_index_id, + window_index_id + (grid_t * llm_grid_h * llm_grid_w) as i64, + grid_thw.device(), + )? + .reshape((grid_t as usize, llm_grid_h as usize, llm_grid_w as usize))? + .contiguous()?; + // python transformers 中实现如下 + // let pad_h = (vit_merger_window_size - llm_grid_h % vit_merger_window_size); + // 后面加上 % vit_merger_window_size,保证llm_grid_h能整除vit_merger_window_size时不需要pad + // 按理说能整除应该是不需要pad的,transformers中这样实现不知道是不是有什么其他原因 + let pad_h = (vit_merger_window_size - llm_grid_h % vit_merger_window_size) + % vit_merger_window_size; + let pad_w = (vit_merger_window_size - llm_grid_w % vit_merger_window_size) + % vit_merger_window_size; + let num_window_h = (llm_grid_h + pad_h) / vit_merger_window_size; + let num_window_w = (llm_grid_w + pad_w) / vit_merger_window_size; + if pad_h > 0 { + let pad_h_t = Tensor::new(vec![-100_i64], grid_thw.device())? + .broadcast_as((grid_t as usize, pad_h as usize, llm_grid_w as usize))? + .contiguous()?; + index = Tensor::cat(&[&index, &pad_h_t], 1)?; + } + if pad_w > 0 { + let pad_w_t = Tensor::new(vec![-100_i64], grid_thw.device())? + .broadcast_as(( + grid_t as usize, + (llm_grid_h + pad_h) as usize, + pad_w as usize, + ))? + .contiguous()?; + index = Tensor::cat(&[&index, &pad_w_t], 2)?; + } + let index_padded = index + .reshape(( + grid_t as usize, + num_window_h as usize, + vit_merger_window_size as usize, + num_window_w as usize, + vit_merger_window_size as usize, + ))? + .permute((0, 1, 3, 2, 4))?; + let index_padded = index_padded + .reshape(( + grid_t as usize, + (num_window_h * num_window_w) as usize, + vit_merger_window_size as usize, + vit_merger_window_size as usize, + ))? + .contiguous()?; + let is_pad = Tensor::new(vec![-100_i64], grid_thw.device())?; + let seqlens = index_padded + .broadcast_ne(&is_pad)? + .sum((2, 3))? + .flatten_all()?; + let index_padded = index_padded.flatten_all()?; + let not_pad = index_padded.broadcast_ne(&is_pad)?.to_vec1::()?; + let indices: Vec = not_pad + .iter() + .enumerate() + .filter_map(|(idx, &val)| if val != 0 { Some(idx as u32) } else { None }) + .collect(); + let indices_tensor = Tensor::from_slice(&indices, indices.len(), grid_thw.device())?; + let index_new = index_padded.gather(&indices_tensor, 0)?; + window_index.push(index_new); + + let seq_len_last = cu_window_seqlens[cu_window_seqlens.len() - 1]; + // cumsum方法i64类型执行会报错,先转成F64计算后再转回i64 + let cu_seqlens_tmp = seqlens + .to_dtype(candle_core::DType::F64)? + .cumsum(0)? + .to_dtype(candle_core::DType::I64)? + .broadcast_mul(&Tensor::new( + vec![self.spatial_merge_unit as i64], + grid_thw.device(), + )?)? + .broadcast_add(&Tensor::new(vec![seq_len_last], grid_thw.device())?)?; + cu_window_seqlens.extend_from_slice(&cu_seqlens_tmp.to_vec1::()?); + window_index_id += (grid_t * llm_grid_h * llm_grid_w) as i64; + } + let window_index_tensor = Tensor::cat(&window_index, 0)?; + let cu_window_seqlens_tensor = Tensor::from_slice( + &cu_window_seqlens, + cu_window_seqlens.len(), + grid_thw.device(), + )? + .to_dtype(candle_core::DType::U32)?; + Ok((window_index_tensor, cu_window_seqlens_tensor)) + } + + pub fn get_attention_mask( + &self, + cu_seqlens: &Tensor, + seq_len: usize, + device: &Device, + dtype: DType, + ) -> Result { + let mut attention_mask = + Tensor::new(f32::NEG_INFINITY, device)?.broadcast_as((1, seq_len, seq_len))?; + for i in 1..cu_seqlens.dim(0)? { + let start = cu_seqlens.i(i - 1)?.to_scalar::()? as usize; + let end = cu_seqlens.i(i)?.to_scalar::()? as usize; + let block_size = end - start; + let zeros = + Tensor::zeros((1, block_size, block_size), candle_core::DType::F32, device)?; + attention_mask = + attention_mask.slice_assign(&[(0..1), (start..end), (start..end)], &zeros)?; + } + let attention_mask = attention_mask.to_dtype(dtype)?.contiguous()?; + Ok(attention_mask) + } + + pub fn forward(&self, hidden_states: &Tensor, grid_thw: &Tensor) -> Result { + // hidden_states: (seq_len, hidden_size) + // grid_thw: (num_images_or_videos, 3), temporal, height, width + let hidden_states = hidden_states.to_dtype(self.dtype)?; + let hidden_states = self.patch_embed.forward(&hidden_states)?; + let rotary_pos_emb = self.rot_pos_emb(grid_thw)?; + let (window_index, cu_window_seqlens) = self.get_window_index(grid_thw)?; + let seq_len = hidden_states.dim(0)?; + let hidden_states = hidden_states + .reshape(( + seq_len / self.spatial_merge_unit, + self.spatial_merge_unit, + (), + ))? + .contiguous()?; + let hidden_states = hidden_states.index_select(&window_index, 0)?; + let mut hidden_states = hidden_states.reshape((seq_len, ()))?; + let rotary_pos_emb = rotary_pos_emb.reshape(( + seq_len / self.spatial_merge_unit, + self.spatial_merge_unit, + (), + ))?; + let rotary_pos_emb = rotary_pos_emb.index_select(&window_index, 0)?; + let rotary_pos_emb = rotary_pos_emb.reshape((seq_len, ()))?; + let emb = Tensor::cat(&[&rotary_pos_emb, &rotary_pos_emb], D::Minus1)?; + let cos = emb.cos()?.to_dtype(hidden_states.dtype())?; + let sin = emb.sin()?.to_dtype(hidden_states.dtype())?; + let cu_seqlens = grid_thw.i((.., 1))?.mul(&grid_thw.i((.., 2))?)?; + let grid_t = grid_thw.i((.., 0))?.to_vec1::()?; + let cu_seqlens_full = match cu_seqlens.rank() { + 1 => cu_seqlens.repeat(grid_t[0] as usize)?, + 2 => { + let mut cu_seqlens_repeat = Vec::new(); + for (index, t) in grid_t.iter().enumerate() { + cu_seqlens_repeat.push(cu_seqlens.i(index)?.repeat(t.clone() as usize)?); + } + Tensor::cat(&cu_seqlens_repeat, 0)?.flatten_all()? + } + _ => { + return Err(anyhow!(format!("create cu_seqlens error"))); + } + }; + let cu_seqlens = cu_seqlens_full + .to_dtype(DType::F64)? + .cumsum(0)? + .to_dtype(DType::U32)?; + let pad_zero = Tensor::from_vec(vec![0_u32], 1, hidden_states.device())?; + let cu_seqlens = Tensor::cat(&[&pad_zero, &cu_seqlens], D::Minus1)?; + let attention_mask_window = self.get_attention_mask( + &cu_window_seqlens, + seq_len, + hidden_states.device(), + hidden_states.dtype(), + )?; + let attention_mask_full = self.get_attention_mask( + &cu_seqlens, + seq_len, + hidden_states.device(), + hidden_states.dtype(), + )?; + let mut attention_mask = attention_mask_window.clone(); + for (layer_num, block) in self.blocks.iter().enumerate() { + if self.fullatt_block_indexes.contains(&layer_num) { + attention_mask = attention_mask_full.clone(); + } else { + attention_mask = attention_mask_window.clone(); + } + hidden_states = block.forward(&hidden_states, &cos, &sin, &attention_mask)?; + } + let hidden_states = self.merger.forward(&hidden_states)?; + let reverse_indices = safe_arg_sort_last_dim(&window_index, true)?; + let hidden_states = hidden_states.index_select(&reverse_indices, 0)?; + Ok(hidden_states) + } +} + +pub fn repeat_kv(xs: Tensor, n_rep: usize) -> Result { + if n_rep == 1 { + Ok(xs) + } else { + let (b_sz, n_kv_head, seq_len, head_dim) = xs.dims4()?; + // Using cat is faster than a broadcast as it avoids going through a potentially + // strided copy. + // https://github.com/huggingface/candle/pull/2043 + let kv = Tensor::cat(&vec![&xs; n_rep], 2)?.reshape(( + b_sz, + n_kv_head * n_rep, + seq_len, + head_dim, + ))?; + Ok(kv) + } +} + +#[derive(Debug, Clone)] +struct Qwen2_5VLTextMLP { + gate_proj: Linear, + up_proj: Linear, + down_proj: Linear, + act_fn: Activation, +} + +impl Qwen2_5VLTextMLP { + fn new(cfg: &Config, vb: VarBuilder) -> Result { + let hidden_sz = cfg.hidden_size; + let intermediate_sz = cfg.intermediate_size; + let gate_proj = linear_no_bias(hidden_sz, intermediate_sz, vb.pp("gate_proj"))?; + let up_proj = linear_no_bias(hidden_sz, intermediate_sz, vb.pp("up_proj"))?; + let down_proj = linear_no_bias(intermediate_sz, hidden_sz, vb.pp("down_proj"))?; + + Ok(Self { + gate_proj, + up_proj, + down_proj, + act_fn: cfg.hidden_act, + }) + } +} + +impl Module for Qwen2_5VLTextMLP { + fn forward(&self, xs: &Tensor) -> candle_core::Result { + let lhs = xs.apply(&self.gate_proj)?.apply(&self.act_fn)?; + let rhs = xs.apply(&self.up_proj)?; + (lhs * rhs)?.apply(&self.down_proj) + } +} + +#[derive(Debug, Clone)] +struct Qwen2_5VLTextAttention { + q_proj: Linear, + k_proj: Linear, + v_proj: Linear, + o_proj: Linear, + num_heads: usize, + num_kv_heads: usize, + num_kv_groups: usize, + head_dim: usize, + hidden_size: usize, + kv_cache: Option<(Tensor, Tensor)>, +} + +impl Qwen2_5VLTextAttention { + fn new(cfg: &Config, vb: VarBuilder) -> Result { + let hidden_size = cfg.hidden_size; + let num_heads = cfg.num_attention_heads; + let num_kv_heads = cfg.num_key_value_heads; + let num_kv_groups = num_heads / num_kv_heads; + let head_dim = hidden_size / num_heads; + let q_proj = linear(hidden_size, num_heads * head_dim, vb.pp("q_proj"))?; + let k_proj = linear(hidden_size, num_kv_heads * head_dim, vb.pp("k_proj"))?; + let v_proj = linear(hidden_size, num_kv_heads * head_dim, vb.pp("v_proj"))?; + let o_proj = linear_no_bias(hidden_size, hidden_size, vb.pp("o_proj"))?; + Ok(Self { + q_proj, + k_proj, + v_proj, + o_proj, + num_heads, + num_kv_heads, + num_kv_groups, + head_dim, + hidden_size, + kv_cache: None, + }) + } + + fn forward( + &mut self, + xs: &Tensor, + cos: &Tensor, + sin: &Tensor, + attention_mask: Option<&Tensor>, + ) -> Result { + let (b_sz, q_len, _) = xs.dims3()?; + let query_states = self.q_proj.forward(xs)?; + let key_states = self.k_proj.forward(xs)?; + let value_states = self.v_proj.forward(xs)?; + let query_states = query_states + .reshape((b_sz, q_len, self.num_heads, self.head_dim))? + .transpose(1, 2)?; + let key_states = key_states + .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))? + .transpose(1, 2)?; + let value_states = value_states + .reshape((b_sz, q_len, self.num_kv_heads, self.head_dim))? + .transpose(1, 2)?; + let (query_states, key_states) = + apply_rotary_pos_emb(&query_states, &key_states, cos, sin)?; + let (key_states, value_states) = match &self.kv_cache { + None => (key_states, value_states), + Some((prev_k, prev_v)) => { + let key_states = Tensor::cat(&[prev_k, &key_states], 2)?; + let value_states = Tensor::cat(&[prev_v, &value_states], 2)?; + (key_states, value_states) + } + }; + + self.kv_cache = Some((key_states.clone(), value_states.clone())); + + let key_states = repeat_kv(key_states, self.num_kv_groups)?.contiguous()?; + let value_states = repeat_kv(value_states, self.num_kv_groups)?.contiguous()?; + let query_states = query_states.contiguous()?; + let attn_output = { + let scale = 1f64 / f64::sqrt(self.head_dim as f64); + #[cfg(not(feature = "flash-attn"))] + { + let attn_weights = + query_states.matmul(&key_states.transpose(D::Minus2, D::Minus1)?)?; + let attn_weights = (attn_weights * scale)?; + let attn_weights = match attention_mask { + None => attn_weights, + Some(mask) => attn_weights.broadcast_add(mask)?, + }; + let attn_weights = candle_nn::ops::softmax_last_dim(&attn_weights)?; + let attn_weights = attn_weights.matmul(&value_states)?; + attn_weights + } + #[cfg(feature = "flash-attn")] + { + // use flash-attn, + // flash-attn shape: (bs, seq_len, num_head, head_dim) + let query_states = query_states.transpose(1, 2)?; + let key_states = key_states.transpose(1, 2)?; + let value_states = value_states.transpose(1, 2)?; + let attn_output = candle_flash_attn::flash_attn( + &query_states, + &key_states, + &value_states, + scale as f32, + attention_mask.is_some(), + )? + .transpose(1, 2)?; + attn_output + } + }; + let attn_output = + attn_output + .transpose(1, 2)? + .contiguous()? + .reshape((b_sz, q_len, self.hidden_size))?; + let attn_output = attn_output.apply(&self.o_proj)?; + Ok(attn_output) + } + + fn clear_kv_cache(&mut self) { + self.kv_cache = None + } +} + +#[derive(Debug, Clone)] +struct Qwen2_5VLTextDecoderLayer { + self_attn: Qwen2_5VLTextAttention, + mlp: Qwen2_5VLTextMLP, + input_layernorm: RmsNorm, + post_attention_layernorm: RmsNorm, +} + +impl Qwen2_5VLTextDecoderLayer { + fn new(cfg: &Config, vb: VarBuilder) -> Result { + let self_attn = Qwen2_5VLTextAttention::new(cfg, vb.pp("self_attn"))?; + let mlp = Qwen2_5VLTextMLP::new(cfg, vb.pp("mlp"))?; + let input_layernorm = + rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("input_layernorm"))?; + let post_attention_layernorm = rms_norm( + cfg.hidden_size, + cfg.rms_norm_eps, + vb.pp("post_attention_layernorm"), + )?; + Ok(Self { + self_attn, + mlp, + input_layernorm, + post_attention_layernorm, + }) + } + + fn forward( + &mut self, + xs: &Tensor, + cos: &Tensor, + sin: &Tensor, + attention_mask: Option<&Tensor>, + ) -> Result { + let residual = xs; + let xs = self.input_layernorm.forward(xs)?; + let xs = self.self_attn.forward(&xs, cos, sin, attention_mask)?; + let xs = (xs + residual)?; + let residual = &xs; + let xs = xs.apply(&self.post_attention_layernorm)?.apply(&self.mlp)?; + let xs = (residual + xs)?; + Ok(xs) + } + + fn clear_kv_cache(&mut self) { + self.self_attn.clear_kv_cache() + } +} + +#[derive(Debug, Clone)] +pub struct Qwen2_5VLTextModel { + pub embed_tokens: candle_nn::Embedding, + layers: Vec, + norm: RmsNorm, + rotary_emb: Qwen2_5VLTextRotaryEmbedding, + dtype: DType, + sliding_window: usize, + device: Device, + rope_scaling: RopeScaling, +} + +impl Qwen2_5VLTextModel { + pub fn new(cfg: &Config, vb: VarBuilder) -> Result { + let embed_tokens = + candle_nn::embedding(cfg.vocab_size, cfg.hidden_size, vb.pp("embed_tokens"))?; + let head_dim = cfg.hidden_size / cfg.num_attention_heads; + let rotary_emb = Qwen2_5VLTextRotaryEmbedding::new(head_dim, cfg.rope_theta); + let mut layers = Vec::with_capacity(cfg.num_hidden_layers); + let vb_l = vb.pp("layers"); + for layer_idx in 0..cfg.num_hidden_layers { + let layer = Qwen2_5VLTextDecoderLayer::new(cfg, vb_l.pp(layer_idx))?; + layers.push(layer) + } + let norm = rms_norm(cfg.hidden_size, cfg.rms_norm_eps, vb.pp("norm"))?; + let sliding_window = cfg.sliding_window; + let rope_scaling = cfg.rope_scaling.clone(); + Ok(Self { + embed_tokens, + layers, + norm, + rotary_emb, + dtype: vb.dtype(), + sliding_window, + device: vb.device().clone(), + rope_scaling, + }) + } + + fn prepare_causal_attention_mask( + &self, + b_size: usize, + tgt_len: usize, + seqlen_offset: usize, + ) -> Result { + // Sliding window mask? + let mask: Vec<_> = (0..tgt_len) + .flat_map(|i| { + (0..tgt_len).map(move |j| { + if i < j || j + self.sliding_window < i { + f32::NEG_INFINITY + } else { + 0. + } + }) + }) + .collect(); + let mask = Tensor::from_slice(&mask, (tgt_len, tgt_len), &self.device)?; + let mask = if seqlen_offset > 0 { + let mask0 = Tensor::zeros((tgt_len, seqlen_offset), self.dtype, &self.device)?; + Tensor::cat(&[&mask0, &mask], D::Minus1)? + } else { + mask + }; + let mask = mask + .expand((b_size, 1, tgt_len, tgt_len + seqlen_offset))? + .to_dtype(self.dtype)?; + Ok(mask) + } + pub fn forward( + &mut self, + inputs_embeds: &Tensor, + seqlen_offset: usize, + position_ids: Option<&Tensor>, + ) -> Result { + let (b_size, seq_len, _) = inputs_embeds.dims3()?; + + let position_ids = match position_ids { + Some(ids) => ids.clone(), + None => Tensor::arange( + seqlen_offset as u32, + (seq_len + seqlen_offset) as u32, + inputs_embeds.device(), + )? + .unsqueeze(0)? + .unsqueeze(0)? + .broadcast_as((3, b_size, seq_len))?, + }; + let (cos, sin) = self.rotary_emb.forward( + &position_ids, + self.dtype, + self.rope_scaling.mrope_section.clone(), + )?; + let mut xs = inputs_embeds.clone(); + let attention_mask: Option<&Tensor> = { + if seq_len <= 1 { + None + } else { + Some(&self.prepare_causal_attention_mask(b_size, seq_len, seqlen_offset)?) + } + }; + for layer in self.layers.iter_mut() { + xs = layer.forward(&xs, &cos, &sin, attention_mask)?; + } + let xs = xs.apply(&self.norm)?; + Ok(xs) + } + + pub fn clear_kv_cache(&mut self) { + for layer in self.layers.iter_mut() { + layer.clear_kv_cache() + } + } +} + +pub struct Qwen2_5VLModel { + visual: Qwen2_5VLVisionModel, + model: Qwen2_5VLTextModel, + pub cfg: Config, + lm_head: Linear, + rope_deltas: Option, +} + +impl Qwen2_5VLModel { + pub fn new(cfg: Config, vb: VarBuilder) -> Result { + let visual = Qwen2_5VLVisionModel::new(&cfg, vb.pp("visual"))?; + let model = Qwen2_5VLTextModel::new(&cfg, vb.pp("model"))?; + let vocab_size = cfg.vocab_size; + let lm_head = if cfg.tie_word_embeddings { + Linear::new(model.embed_tokens.embeddings().clone(), None) + } else { + linear_no_bias(cfg.hidden_size, vocab_size, vb.pp("lm_head"))? + }; + + Ok(Self { + visual, + model, + cfg, + lm_head, + rope_deltas: None, + }) + } + + pub fn get_rope_index( + &self, + input_ids: &Tensor, + image_grid_thw: Option<&Tensor>, + video_grid_thw: Option<&Tensor>, + mask: Option<&Tensor>, + second_per_grid_ts: Option>, + ) -> Result<(Tensor, Tensor)> { + let spatial_merge_size = self.cfg.vision_config.spatial_merge_size; + let mut mrope_position_deltas: Vec = Vec::new(); + if image_grid_thw.is_some() || video_grid_thw.is_some() { + let total_input_ids = input_ids.clone(); + let mut mask_; + if mask.is_none() { + mask_ = Tensor::ones_like(&total_input_ids)?; + } else { + mask_ = mask.unwrap().clone(); + } + let mut position_ids = Tensor::ones( + (3, input_ids.dim(0)?, input_ids.dim(1)?), + input_ids.dtype(), + input_ids.device(), + )?; + let mut image_index = 0; + let mut video_index = 0; + for i in 0..total_input_ids.dim(0)? { + let mut input_ids_i = total_input_ids.i(i)?; + let mask_i = mask_.i(i)?; + // 推理时, attention_mask如果是全1向量,取非0索引的操作没必要 + if mask_i.sum_all()?.to_scalar::()? != mask_i.dim(0)? as u32 { + let nonzero_idx = nonzero_index(&mask_i)?; + input_ids_i = input_ids_i.gather(&nonzero_idx, 0)?; + } + let mut text_start = 0; + let mut text_end = 0; + let mut thw = vec![]; + let mut second_per_grid_t = 0_f32; + let mut llm_pos_ids_list: Vec = Vec::new(); + // vision start的下一个索引 + let vision_indices = + get_vision_next_indices(&input_ids_i, self.cfg.vision_start_token_id as u32); + match vision_indices { + Ok(indeices) => { + let vision_tokens = input_ids_i.gather(&indeices, 0)?.to_vec1::()?; + let vision_indices_vec = indeices.to_vec1::()?; + for (j, &token) in vision_tokens.iter().enumerate() { + if token == self.cfg.image_token_id as u32 { + thw = image_grid_thw.unwrap().i(image_index)?.to_vec1::()?; + image_index += 1; + text_end = vision_indices_vec[j]; + second_per_grid_t = 0.0; + } + if token == self.cfg.video_token_id as u32 { + thw = video_grid_thw.unwrap().i(video_index)?.to_vec1::()?; + text_end = vision_indices_vec[j]; + second_per_grid_t = match second_per_grid_ts { + None => 1.0, + Some(ref vec) => vec[video_index], + }; + video_index += 1; + } + let llm_grid_t = thw[0]; + let llm_grid_h = thw[1] / spatial_merge_size as u32; + let llm_grid_w = thw[2] / spatial_merge_size as u32; + let text_len = text_end - text_start; + let start_idx = if llm_pos_ids_list.len() > 0 { + llm_pos_ids_list[llm_pos_ids_list.len() - 1] + .max_all()? + .to_scalar::()? + + 1 + } else { + 0 + }; + let pos_ids = Tensor::arange( + start_idx, + start_idx + text_len, + input_ids_i.device(), + )? + .unsqueeze(0)? + .broadcast_as((3usize, text_len as usize))?; + llm_pos_ids_list.push(pos_ids); + let range_tensor = Tensor::arange(0, llm_grid_t, input_ids_i.device())? + .unsqueeze(D::Minus1)?; + let expanded_range = range_tensor.broadcast_as(( + llm_grid_t as usize, + (llm_grid_h * llm_grid_w) as usize, + ))?; + let time_tensor = expanded_range + .broadcast_mul(&Tensor::new( + vec![ + (second_per_grid_t + * self.cfg.vision_config.tokens_per_second as f32) + as u32, + ], + input_ids_i.device(), + )?)? + .broadcast_add(&Tensor::new( + vec![start_idx + text_len], + input_ids_i.device(), + )?)?; + let t_index = time_tensor.flatten_all()?; + let h_index = Tensor::arange( + start_idx + text_len, + start_idx + text_len + llm_grid_h, + input_ids_i.device(), + )? + .unsqueeze(0)? + .unsqueeze(D::Minus1)? + .broadcast_as(( + llm_grid_t as usize, + llm_grid_h as usize, + llm_grid_w as usize, + ))? + .flatten_all()?; + let w_index = Tensor::arange( + start_idx + text_len, + start_idx + text_len + llm_grid_w, + input_ids_i.device(), + )? + .unsqueeze(0)? + .unsqueeze(0)? + .broadcast_as(( + llm_grid_t as usize, + llm_grid_h as usize, + llm_grid_w as usize, + ))? + .flatten_all()?; + + let thw_index = Tensor::stack(&[t_index, h_index, w_index], 0)?; + llm_pos_ids_list.push(thw_index); + text_start = text_end + llm_grid_t * llm_grid_h * llm_grid_w; + } + } + Err(e) => { + println!("get vision_indices err: {}", e); + } + }; + + if text_start < input_ids_i.dim(0)? as u32 { + let start_idx = if llm_pos_ids_list.len() > 0 { + llm_pos_ids_list[llm_pos_ids_list.len() - 1] + .max_all()? + .to_scalar::()? + + 1 + } else { + 0 + }; + let text_len = input_ids_i.dim(0)? as u32 - text_start; + let pos_ids = + Tensor::arange(start_idx, start_idx + text_len, input_ids_i.device())? + .unsqueeze(0)? + .broadcast_as((3usize, text_len as usize))?; + llm_pos_ids_list.push(pos_ids); + } + let llm_position = Tensor::cat(&llm_pos_ids_list, 1)?.reshape((3, 1, ()))?; + position_ids = position_ids + .slice_assign(&[(0..3), (i..i + 1), (0..input_ids.dim(1)?)], &llm_position)?; + let position_deltas = llm_position.max_all()?.to_scalar::()? as i64 + 1 + - input_ids_i.dim(0)? as i64; + mrope_position_deltas.push(position_deltas); + } + + let mut mrope_position_deltas = Tensor::new(mrope_position_deltas, input_ids.device())?; + if mrope_position_deltas.rank() == 1 { + mrope_position_deltas = mrope_position_deltas.unsqueeze(0)?; + } + return Ok((position_ids.contiguous()?, mrope_position_deltas)); + } else { + if mask.is_some() { + let mut position_ids = mask + .unwrap() + .to_dtype(candle_core::DType::F64)? + .cumsum(D::Minus1)? + .to_dtype(candle_core::DType::U32)? + .broadcast_sub(&Tensor::new(vec![1_u32], input_ids.device())?)?; + for i in 0..position_ids.dim(0)? { + let mut position_ids_i = position_ids.i(i)?; + let mask_i = mask.unwrap().i(i)?; + // 如果有pad, 将填充位置置为1 + // 当bs>1, 可能存在不同序列长度,需要添加pad使seq_len长度一致 + if mask_i.sum_all()?.to_scalar::()? != mask_i.dim(0)? as u32 { + let zero_indices = zero_index(&mask_i)?; + let replace_1 = Tensor::ones( + zero_indices.dim(0)?, + candle_core::DType::U32, + input_ids.device(), + )?; + position_ids_i = position_ids_i + .scatter(&zero_indices, &replace_1, 0)? + .unsqueeze(0)?; + position_ids = position_ids.slice_assign( + &[(i..i + 1), (0..position_ids.dim(1)?)], + &position_ids_i, + )?; + } + } + position_ids = position_ids + .unsqueeze(0)? + .broadcast_as((3, input_ids.dim(0)?, input_ids.dim(1)?))? + .contiguous()?; + let mut mrope_position_deltas = position_ids + .max(0)? + .max(D::Minus1)? + .broadcast_sub(&Tensor::new( + vec![mask.unwrap().dim(D::Minus1)? as u32 - 1], + input_ids.device(), + )?)? + .contiguous()?; + if mrope_position_deltas.rank() == 1 { + mrope_position_deltas = mrope_position_deltas.unsqueeze(0)?; + } + return Ok((position_ids, mrope_position_deltas)); + } else { + let position_ids = + Tensor::arange(0_u32, input_ids.dim(D::Minus1)? as u32, input_ids.device())? + .unsqueeze(0)? + .unsqueeze(0)? + .broadcast_as((3, input_ids.dim(0)?, input_ids.dim(D::Minus1)?))? + .contiguous()?; + let mrope_position_deltas = Tensor::zeros( + (input_ids.dim(0)?, 1), + input_ids.dtype(), + input_ids.device(), + )?; + Ok((position_ids, mrope_position_deltas)) + } + } + } + + pub fn forward( + &mut self, + input_ids: &Tensor, + pixel_values: Option<&Tensor>, + image_grid_thw: Option<&Tensor>, + pixel_values_video: Option<&Tensor>, + video_grid_thw: Option<&Tensor>, + mask: &Tensor, + cache_position: Option<&Tensor>, + seqlen_offset: usize, + second_per_grid_ts: Option>, + ) -> Result { + // input_ids shape: (bs, seq_len) + let mut inputs_embeds = self.model.embed_tokens.forward(&input_ids)?; + // inputs_embeds shape: (bs, seq_len, hidden_dim) + if pixel_values.is_some() && image_grid_thw.is_some() { + // image_embed shape: (seq_len, hidden_dim) + let image_embed = self + .visual + .forward(pixel_values.unwrap(), image_grid_thw.unwrap())?; + let vision_mask = get_equal_mask(&input_ids, self.cfg.image_token_id as u32)?; + + let n_image_tokens = vision_mask.sum_all()?.to_scalar::()?; + if n_image_tokens as usize != image_embed.dim(0)? { + return Err(anyhow!(format!( + "n_image_token num: {} not equal to image_embed len: {}", + n_image_tokens, + image_embed.dim(0)? + ))); + } + inputs_embeds = masked_scatter_dim0(&inputs_embeds, &image_embed, &vision_mask)?; + } + if pixel_values_video.is_some() && video_grid_thw.is_some() { + let video_embed = self + .visual + .forward(pixel_values_video.unwrap(), video_grid_thw.unwrap())?; + + let vision_mask = get_equal_mask(&input_ids, self.cfg.video_token_id as u32)?; + let n_video_tokens = vision_mask.sum_all()?.to_scalar::()?; + if n_video_tokens as usize != video_embed.dim(0)? { + return Err(anyhow!(format!( + "n_image_token num: {} not equal to image_embed len: {}", + n_video_tokens, + video_embed.dim(0)? + ))); + } + inputs_embeds = masked_scatter_dim0(&inputs_embeds, &video_embed, &vision_mask)?; + } + let mut position_ids; + let mut rope_deltas; + if (cache_position.is_some() && cache_position.unwrap().i(0)?.to_scalar::()? == 0) + || self.rope_deltas.is_none() + { + (position_ids, rope_deltas) = self.get_rope_index( + input_ids, + image_grid_thw, + video_grid_thw, + Some(mask), + second_per_grid_ts, + )?; + self.rope_deltas = Some(rope_deltas); + } else { + let (bs, seq_len, _) = inputs_embeds.dims3()?; + let delta = if cache_position.is_some() { + cache_position + .unwrap() + .i(0)? + .to_dtype(self.rope_deltas.as_ref().unwrap().dtype())? + .broadcast_add(&self.rope_deltas.as_ref().unwrap())? + .contiguous()? + .to_dtype(candle_core::DType::U32)? + } else { + Tensor::zeros(1, inputs_embeds.dtype(), inputs_embeds.device())? + }; + position_ids = Tensor::arange(0u32, seq_len as u32, input_ids.device())? + .unsqueeze(0)? + .broadcast_as((bs, seq_len))? + .broadcast_add(&delta)? + .unsqueeze(0)? + .broadcast_as((3, bs, seq_len))? + .contiguous()?; + } + let outputs = self + .model + .forward(&inputs_embeds, seqlen_offset, Some(&position_ids))?; + let seq_len = outputs.dim(1)?; + let hidden_state = outputs.narrow(1, seq_len - 1, 1)?; + let logits = self.lm_head.forward(&hidden_state)?; + Ok(logits) + } + pub fn clear_kv_cache(&mut self) { + self.model.clear_kv_cache(); + } +} diff --git a/src/models/qwen2_5vl/processor.rs b/src/models/qwen2_5vl/processor.rs new file mode 100644 index 0000000..eb173d1 --- /dev/null +++ b/src/models/qwen2_5vl/processor.rs @@ -0,0 +1,462 @@ +use std::collections::HashMap; + +use anyhow::{Result, anyhow}; +use candle_core::{DType, Device, IndexOp, Shape, Tensor}; +use ffmpeg_next as ffmpeg; +use image::DynamicImage; +use num::integer::lcm; +use openai_dive::v1::resources::chat::{ + ChatCompletionParameters, ChatMessage, ChatMessageContent, ChatMessageContentPart, +}; + +use crate::{ + models::qwen2_5vl::config::VisionSetting, + utils::{ + img_utils::get_image, + utils::{ceil_by_factor, floor_by_factor, round_by_factor}, + }, +}; + +#[derive(Clone)] +pub struct VisionInput { + pub data: Tensor, + pub grid_thw: Tensor, +} + +#[derive(Clone)] +pub struct GeneralInput { + pub replace_text: String, + pub pixel_values: Option, + pub image_grid_thw: Option, + pub pixel_values_video: Option, + pub video_grid_thw: Option, + pub second_per_grid_ts: Option>, +} + +pub struct Qwen2_5VLProcessor { + vision_setting: VisionSetting, + device: Device, + dtype: DType, + image_token: String, + video_token: String, +} + +impl Qwen2_5VLProcessor { + pub fn new(device: &Device, dtype: DType) -> Result { + let vision_setting = VisionSetting::default(); + let image_token = "<|image_pad|>".to_string(); + let video_token = "<|video_pad|>".to_string(); + Ok(Self { + vision_setting, + device: device.clone(), + dtype, + image_token, + video_token, + }) + } + + pub fn extract_vision_info( + &self, + mes: &ChatCompletionParameters, + ) -> Result>> { + let mut vision_map = HashMap::new(); + vision_map.insert("image".to_string(), Vec::new()); + vision_map.insert("video".to_string(), Vec::new()); + for chat_mes in mes.messages.clone() { + match chat_mes { + ChatMessage::User { content, name } => match content { + ChatMessageContent::ContentPart(part_vec) => { + for part in part_vec { + match part { + ChatMessageContentPart::Image(img_part) => { + let img_url = img_part.image_url; + vision_map.get_mut("image").unwrap().push(img_url.url); + } + _ => {} + } + } + } + _ => {} + }, + _ => {} + } + } + Ok(vision_map) + } + + pub fn process_img( + &self, + img: &DynamicImage, + img_mean: &Tensor, + img_std: &Tensor, + ) -> Result { + let img_h = img.height(); + let img_w = img.width(); + // h,w resize成 28的倍数 + let (resize_h, resize_w) = smart_resize(img_h, img_w, &self.vision_setting, true, None)?; + let img = img.resize_exact(resize_w, resize_h, image::imageops::FilterType::CatmullRom); + let img_vec = img.to_rgb8().into_raw(); + // (h, w, c) => (c, h, w) + let img_tensor = Tensor::from_slice( + &img_vec, + (resize_h as usize, resize_w as usize, 3), + &self.device, + )? + .permute((2, 0, 1))? + .to_dtype(self.dtype)?; + // 0-255 rescale to 0-1 + let img_tensor = img_tensor.affine(1.0 / 255.0, 0.)?; + // normalize + let img_tensor = img_tensor + .broadcast_sub(&img_mean)? + .broadcast_div(&img_std)?; + // (c, h, w) => (1, c, h, w) + let img_tensor = img_tensor.unsqueeze(0)?; + Ok(img_tensor) + } + + pub fn process_vision_tensor(&self, img_tensor: &Tensor) -> Result<(Tensor, Tensor)> { + let channel = img_tensor.dim(1)?; + let grid_t = img_tensor.dim(0)? / self.vision_setting.temporal_patch_size; + let grid_h = img_tensor.dim(2)? / self.vision_setting.patch_size; + let grid_w = img_tensor.dim(3)? / self.vision_setting.patch_size; + let shape = Shape::from(vec![ + grid_t, + self.vision_setting.temporal_patch_size, + channel, + grid_h / self.vision_setting.merge_size, + self.vision_setting.merge_size, + self.vision_setting.patch_size, + grid_w / self.vision_setting.merge_size, + self.vision_setting.merge_size, + self.vision_setting.patch_size, + ]); + let img_tensor = img_tensor.reshape(shape)?; + // shape to // grid_t, + // grid_h / merge_size, + // grid_w / merge_size, + // merge_size, + // merge_size, + // channel, + // temporal_patch_size, + // patch_size, + // patch_size, + let img_tensor = img_tensor.permute(vec![0, 3, 6, 4, 7, 2, 1, 5, 8])?; + let img_tensor = img_tensor + .reshape(( + grid_t * grid_h * grid_w, + channel + * self.vision_setting.temporal_patch_size + * self.vision_setting.patch_size + * self.vision_setting.patch_size, + ))? + .contiguous()?; + let grid_thw = Tensor::from_vec( + vec![grid_t as u32, grid_h as u32, grid_w as u32], + (1, 3), + &self.device, + )?; + Ok((img_tensor, grid_thw)) + } + + pub fn process_images( + &self, + imgs: Vec, + img_mean: &Tensor, + img_std: &Tensor, + ) -> Result { + let mut pixel_values_vec = Vec::new(); + let mut vision_grid_thws_vec = Vec::new(); + + for img in imgs { + let img_tensor = self.process_img(&img, &img_mean, &img_std)?; + let img_tensor = Tensor::cat(&[&img_tensor, &img_tensor], 0)?.contiguous()?; + let (img_tensor, grid_thw) = self.process_vision_tensor(&img_tensor)?; + pixel_values_vec.push(img_tensor); + vision_grid_thws_vec.push(grid_thw); + } + let pixel_values = Tensor::cat(&pixel_values_vec, 0)?; + let vision_grid_thws = Tensor::cat(&vision_grid_thws_vec, 0)?; + Ok(VisionInput { + data: pixel_values, + grid_thw: vision_grid_thws, + }) + } + + pub fn process_videos( + &self, + data: Vec, + img_mean: &Tensor, + img_std: &Tensor, + ) -> Result { + let mut pixel_values_vec = Vec::new(); + let mut vision_grid_thws_vec = Vec::new(); + for single_video in data { + // 0-255 rescale to 0-1 + let video_tensor = single_video.to_dtype(self.dtype)?.affine(1.0 / 255.0, 0.)?; + // normalize + let video_tensor = video_tensor + .broadcast_sub(&img_mean)? + .broadcast_div(&img_std)? + .contiguous()?; + let (video_tensor, video_grid_thw) = self.process_vision_tensor(&video_tensor)?; + pixel_values_vec.push(video_tensor); + vision_grid_thws_vec.push(video_grid_thw); + } + let pixel_values = Tensor::cat(&pixel_values_vec, 0)?.contiguous()?; + let vision_grid_thws = Tensor::cat(&vision_grid_thws_vec, 0)?.contiguous()?; + Ok(VisionInput { + data: pixel_values, + grid_thw: vision_grid_thws, + }) + } + + pub fn process_info( + &self, + messages: &ChatCompletionParameters, + text: &str, + ) -> Result { + let mut pixel_values = None; + let mut image_grid_thw = None; + let mut pixel_values_video = None; + let mut video_grid_thw = None; + let mut second_per_grid_ts = None; + let vision_map = self.extract_vision_info(messages)?; + let img_mean = + Tensor::from_slice(&self.vision_setting.image_mean, (3, 1, 1), &self.device)? + .to_dtype(self.dtype)?; + let img_std = Tensor::from_slice(&self.vision_setting.image_std, (3, 1, 1), &self.device)? + .to_dtype(self.dtype)?; + for (key, vec) in vision_map { + // println!("key: {}, \nvalue: {:?}", key, vec); + if key.eq("image") { + let mut file_vec = Vec::new(); + for file in &vec { + let image = get_image(file); + match image { + Ok(img) => file_vec.push(img), + Err(e) => println!("get_image err: {:?}", e), + }; + } + if file_vec.len() > 0 { + let vision_input = self.process_images(file_vec, &img_mean, &img_std); + match vision_input { + Ok(img_input) => { + pixel_values = Some(img_input.data); + image_grid_thw = Some(img_input.grid_thw); + } + Err(e) => println!("img process_images err: {:?}", e), + }; + } + } + if key.eq("video") { + let mut file_vec = Vec::new(); + for file in &vec { + let video_data = get_video_data(file, &self.vision_setting, &self.device); + match video_data { + Ok(tensor) => file_vec.push(tensor), + Err(e) => println!("get_video_data err: {:?}", e), + }; + } + if file_vec.len() > 0 { + let vision_input = self.process_videos(file_vec, &img_mean, &img_std); + match vision_input { + Ok(video_input) => { + let video_num = video_input.grid_thw.dim(0)?; + pixel_values_video = Some(video_input.data); + video_grid_thw = Some(video_input.grid_thw); + let second_per_grid = vec![ + self.vision_setting.temporal_patch_size + as f32 + / self.vision_setting.fps; + video_num + ]; + second_per_grid_ts = Some(second_per_grid); + } + Err(e) => println!("video process_videos err: {:?}", e), + }; + } + } + } + let merge_length = self.vision_setting.merge_size.pow(2); + let mut text = text.to_string(); + if image_grid_thw.is_some() { + let mut index = 0; + while text.contains(&self.image_token) { + let grid_i = image_grid_thw.as_ref().unwrap().i(index)?; + let repeat_num = + grid_i.to_vec1::()?.iter().product::() as usize / merge_length; + let replace = "<|placeholder|>".repeat(repeat_num); + text = text.replacen(&self.image_token, &replace, 1); + index += 1; + } + text = text.replace("<|placeholder|>", &self.image_token); + } + if video_grid_thw.is_some() { + let mut index = 0; + while text.contains(&self.video_token) { + let grid_i = video_grid_thw.as_ref().unwrap().i(index)?; + let repeat_num = + grid_i.to_vec1::()?.iter().product::() as usize / merge_length; + let replace = "<|placeholder|>".repeat(repeat_num); + text = text.replacen(&self.video_token, &replace, 1); + index += 1; + } + text = text.replace("<|placeholder|>", &self.video_token); + } + let input = GeneralInput { + replace_text: text, + pixel_values, + image_grid_thw, + pixel_values_video, + video_grid_thw, + second_per_grid_ts, + }; + Ok(input) + } +} + +pub fn smart_resize( + img_h: u32, + img_w: u32, + vision_setting: &VisionSetting, + is_img: bool, + video_ratio: Option, +) -> Result<(u32, u32)> { + if std::cmp::max(img_h, img_w) / std::cmp::min(img_h, img_w) > vision_setting.max_ratio { + return Err(anyhow!(format!( + "absolute aspect ratio mush be smaller than {}, got {}", + vision_setting.max_ratio, + std::cmp::max(img_h, img_w) / std::cmp::min(img_h, img_w) + ))); + } + let mut image_factor = vision_setting.image_factor; + if let Some(ratio) = video_ratio { + image_factor = lcm(image_factor, ratio); + } + let mut h_bar = std::cmp::max(image_factor, round_by_factor(img_h, image_factor)); + let mut w_bar = std::cmp::max(image_factor, round_by_factor(img_w, image_factor)); + let mut max_pixels = 0u32; + let mut min_pixels = 0u32; + if is_img { + min_pixels = vision_setting.min_pixels; + max_pixels = vision_setting.max_pixels; + } else { + min_pixels = vision_setting.video_min_pixels; + max_pixels = vision_setting.video_max_pixels; + } + if h_bar * w_bar > max_pixels { + let beta = ((img_h * img_w) as f32 / max_pixels as f32).sqrt(); + h_bar = floor_by_factor(img_h as f32 / beta, image_factor); + w_bar = floor_by_factor(img_w as f32 / beta, image_factor); + } else if h_bar * w_bar < min_pixels { + let beta = (min_pixels as f32 / (img_h * img_w) as f32).sqrt(); + h_bar = ceil_by_factor(img_h as f32 * beta, image_factor); + w_bar = ceil_by_factor(img_w as f32 * beta, image_factor); + } + Ok((h_bar, w_bar)) +} + +pub fn get_video_data( + file: &String, + vision_setting: &VisionSetting, + device: &Device, +) -> Result { + ffmpeg::init().map_err(|e| anyhow!(format!("Failed to initialize ffmpeg: {}", e)))?; + + let mut ictx = ffmpeg::format::input(&file) + .map_err(|e| anyhow!(format!("Failed to open video file: {}", e)))?; + let input = ictx + .streams() + .best(ffmpeg::media::Type::Video) + .ok_or_else(|| anyhow!(format!("No video stream found")))?; + let video_stream_index = input.index(); + let context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters()) + .map_err(|e| anyhow!(format!("Failed to crate decoder context: {}", e)))?; + let mut decoder = context_decoder + .decoder() + .video() + .map_err(|e| anyhow!(format!("Failed to decoder video: {}", e)))?; + + let video_h = decoder.height(); + let video_w = decoder.width(); + let format = decoder.format(); + + let frames = input.frames(); + let rate = (input.rate().0 as f32 / input.rate().1 as f32).round() as u32; + // 1s取两帧 + let min_frames = ceil_by_factor( + vision_setting.fps_min_frames as f32, + vision_setting.frame_factor, + ); + let max_frames = floor_by_factor( + vision_setting.fps_max_frames as f32, + vision_setting.frame_factor, + ); + let nframes = (frames as f32 / rate as f32 * vision_setting.fps) as u32; + let nframes = std::cmp::min(std::cmp::max(nframes, min_frames), max_frames); + let nframes = round_by_factor(nframes, vision_setting.frame_factor); + let sample_interval = (frames as f32 / nframes as f32).round() as u32; + let mut frame_id = 0_u32; + + // 图片帧使用scaler reshape的时候需要保证宽高是16的倍数,不然reshape出来的是损坏的图片 + // 所以计算resize的目标宽高时,需要用16和image_factor的最小公倍数 + let (resize_h, resize_w) = smart_resize(video_h, video_w, vision_setting, false, Some(16))?; + let mut scaler = ffmpeg::software::scaling::context::Context::get( + format, + video_w, + video_h, + ffmpeg::format::Pixel::RGB24, + resize_w, + resize_h, + ffmpeg::software::scaling::flag::Flags::BILINEAR + | ffmpeg::software::scaling::flag::Flags::ACCURATE_RND, + ) + .map_err(|e| anyhow!(format!("Failed to crate scaler: {}", e)))?; + + let mut frames_vec = Vec::new(); + let mut receive_and_process_decoded_frames = + |decoder: &mut ffmpeg::decoder::Video| -> Result<()> { + let mut decoded = ffmpeg::frame::Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + if frame_id % sample_interval == 0 { + let mut rgb_frame = ffmpeg::frame::Video::empty(); + scaler + .run(&decoded, &mut rgb_frame) + .map_err(|e| anyhow!(format!("Failed to scaler run decoded: {}", e)))?; + + // save_file(&rgb_frame, frame_id as usize); + let frame_data = rgb_frame.data(0); + let frame_tensor = Tensor::from_slice( + frame_data, + (resize_h as usize, resize_w as usize, 3), + device, + )? + .permute((2, 0, 1))?; + frames_vec.push(frame_tensor); + } + frame_id += 1; + } + Ok(()) + }; + + for (stream, packet) in ictx.packets() { + if stream.index() == video_stream_index { + decoder + .send_packet(&packet) + .map_err(|e| anyhow!(format!("Failed to send packet: {}", e)))?; + receive_and_process_decoded_frames(&mut decoder)?; + } + } + decoder + .send_eof() + .map_err(|e| anyhow!(format!("Failed to decoder.send_eof(): {}", e)))?; + receive_and_process_decoded_frames(&mut decoder)?; + + if frames_vec.is_empty() { + return Err(anyhow!("No frames extracted from video".to_string())); + } + // (t, c, h, w) + let frames_tensor = Tensor::stack(&frames_vec, 0)?.contiguous()?; + Ok(frames_tensor) +} diff --git a/src/position_embed/mod.rs b/src/position_embed/mod.rs new file mode 100644 index 0000000..f67f4f0 --- /dev/null +++ b/src/position_embed/mod.rs @@ -0,0 +1 @@ +pub mod rope; diff --git a/src/position_embed/rope.rs b/src/position_embed/rope.rs new file mode 100644 index 0000000..165bcf0 --- /dev/null +++ b/src/position_embed/rope.rs @@ -0,0 +1,182 @@ +use anyhow::Result; +use candle_core::{D, DType, Device, IndexOp, Tensor}; +use candle_transformers::models::deepseek2::SplitOp; + +use crate::models::qwen2_5vl::config::RopeScaling; + +pub fn compute_default_rope_parameters(dim: usize, base: f32) -> Vec { + let inv_freq: Vec = (0..dim) + .step_by(2) + .map(|i| 1.0_f32 / base.powf(i as f32 / dim as f32)) + .collect(); + inv_freq +} + +pub fn rotate_half(x: &Tensor) -> Result { + let half_dim = x.dim(D::Minus1)? / 2; + let x1 = x.narrow(D::Minus1, 0, half_dim)?; + let x2 = x.narrow(D::Minus1, half_dim, half_dim)?; + let x2 = x2.affine(-1.0, 0.0)?; + let rotate_x = Tensor::cat(&[&x2, &x1], D::Minus1)?.contiguous()?; + Ok(rotate_x) +} + +pub fn apply_multimodel_rotary_pos_emb( + q: &Tensor, + k: &Tensor, + cos: &Tensor, + sin: &Tensor, + mrope_section: Vec, +) -> Result<(Tensor, Tensor)> { + let mrope_section = mrope_section.repeat(2); + let cos_select: Vec = cos + .split(&mrope_section, D::Minus1)? + .iter() + .enumerate() + .map(|(i, m)| m.i(i % 3).unwrap()) + .collect(); + let cos = Tensor::cat(&cos_select, D::Minus1)? + .unsqueeze(1)? + .contiguous()?; + let sin_select: Vec = sin + .split(&mrope_section, D::Minus1)? + .iter() + .enumerate() + .map(|(i, m)| m.i(i % 3).unwrap()) + .collect(); + let sin = Tensor::cat(&sin_select, D::Minus1)? + .unsqueeze(1)? + .contiguous()?; + let q_embed = q + .broadcast_mul(&cos)? + .add(&rotate_half(&q)?.broadcast_mul(&sin)?)?; + let k_embed = k + .broadcast_mul(&cos)? + .add(&rotate_half(&k)?.broadcast_mul(&sin)?)?; + Ok((q_embed, k_embed)) +} + +pub fn apply_rotary_pos_emb_vision( + q: &Tensor, + k: &Tensor, + cos: &Tensor, + sin: &Tensor, +) -> Result<(Tensor, Tensor)> { + // q, k -> (seq_len, num_heads, head_dim) + // cos, sin -> (seq_len, head_dim) -> (seq_len, 1, head_dim) + let cos = cos.unsqueeze(D::Minus2)?; + let sin = sin.unsqueeze(D::Minus2)?; + let q_embed = q + .broadcast_mul(&cos)? + .add(&rotate_half(q)?.broadcast_mul(&sin)?)?; + let k_embed = k + .broadcast_mul(&cos)? + .add(&rotate_half(k)?.broadcast_mul(&sin)?)?; + Ok((q_embed, k_embed)) +} + +pub fn apply_rotary_pos_emb( + q: &Tensor, + k: &Tensor, + cos: &Tensor, + sin: &Tensor, +) -> Result<(Tensor, Tensor)> { + // sin/cos: (bs, 1, seq_len, head_dim) + // q/k: (bs, n_head, seq_len, head_dim) + let q_embed = q + .broadcast_mul(&cos)? + .add(&rotate_half(q)?.broadcast_mul(&sin)?)?; + let k_embed = k + .broadcast_mul(&cos)? + .add(&rotate_half(k)?.broadcast_mul(&sin)?)?; + Ok((q_embed, k_embed)) +} + +#[derive(Debug, Clone)] +pub struct Qwen2_5VLTextRotaryEmbedding { + inv_freq: Vec, +} + +impl Qwen2_5VLTextRotaryEmbedding { + pub fn new(dim: usize, theta_base: f32) -> Self { + let inv_freq = compute_default_rope_parameters(dim, theta_base); + Self { inv_freq } + } + pub fn forward( + &self, + position_ids: &Tensor, + dtype: DType, + mrope_section: Vec, + ) -> Result<(Tensor, Tensor)> { + // position_ids shape: (3, bs, position) -> (3, bs, 1, position) + let position_ids_expanded = position_ids + .unsqueeze(D::Minus2)? + .to_dtype(DType::F32)? + .contiguous()?; + // inv_freq Vec -> Tensor(1, 1, head_dim / 2, 1) -> (3, bs, head_dim / 2, 1) + let inv_freq_expanded = Tensor::from_vec( + self.inv_freq.clone(), + (1, 1, self.inv_freq.len(), 1), + position_ids.device(), + )? + .broadcast_as((3, position_ids.dim(1)?, self.inv_freq.len(), 1))? + .to_dtype(DType::F32)? + .contiguous()?; + + // (3, bs, head_dim / 2, 1) matmul (3, bs, 1, position) + // -> (3, bs, head_dim / 2, seq_len) -> (3, bs, seq_len, head_dim / 2) + let freqs = inv_freq_expanded + .matmul(&position_ids_expanded)? + .transpose(2, 3)?; + // let freqs = position_ids_expanded.matmul(&inv_freq_expanded)?; + // (3, bs, seq_len, head_dim / 2) -> (3, bs, seq_len, head_dim) + let emb = Tensor::cat(&[&freqs, &freqs], D::Minus1)?.contiguous()?; + let cos = emb.cos()?; + let sin = emb.sin()?; + let mrope_section = mrope_section.repeat(2); + let cos_select: Vec = cos + .split(&mrope_section, D::Minus1)? + .iter() + .enumerate() + .map(|(i, m)| m.i(i % 3).unwrap()) + .collect(); + // (bs, seq_len, head_dim) -> (bs, 1, seq_len, head_dim) + let cos = Tensor::cat(&cos_select, D::Minus1)? + .unsqueeze(1)? + .contiguous()?; + let sin_select: Vec = sin + .split(&mrope_section, D::Minus1)? + .iter() + .enumerate() + .map(|(i, m)| m.i(i % 3).unwrap()) + .collect(); + // (bs, seq_len, head_dim) -> (bs, 1, seq_len, head_dim) + let sin = Tensor::cat(&sin_select, D::Minus1)? + .unsqueeze(1)? + .contiguous()?; + Ok((cos.to_dtype(dtype)?, sin.to_dtype(dtype)?)) + } +} + +#[derive(Debug, Clone)] +pub struct Qwen2_5VisionRotaryEmbedding { + inv_freq: Vec, +} + +impl Qwen2_5VisionRotaryEmbedding { + pub fn new(dim: usize, theta_base: Option) -> Self { + let theta_base = match theta_base { + Some(theta) => theta, + None => 10000.0_f32, + }; + let inv_freq = compute_default_rope_parameters(dim, theta_base); + Self { inv_freq } + } + + pub fn forward(&self, seqlen: usize, device: &Device) -> Result { + let seq = Tensor::arange(0.0_f32, seqlen as f32, device)?.reshape((seqlen, 1))?; + let inv_freq = Tensor::from_vec(self.inv_freq.clone(), (1, self.inv_freq.len()), device)?; + let freqs = seq.matmul(&inv_freq)?; + Ok(freqs) + } +} diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs new file mode 100644 index 0000000..5768976 --- /dev/null +++ b/src/tokenizer/mod.rs @@ -0,0 +1 @@ +pub mod tokenizer; \ No newline at end of file diff --git a/src/tokenizer/tokenizer.rs b/src/tokenizer/tokenizer.rs new file mode 100644 index 0000000..8040a76 --- /dev/null +++ b/src/tokenizer/tokenizer.rs @@ -0,0 +1,44 @@ +use anyhow::{Result, anyhow}; +use candle_core::{Device, Tensor}; +use tokenizers::Tokenizer; + +pub struct TokenizerModel { + tokenizer: Tokenizer, +} + +impl TokenizerModel { + pub fn init(path: &str) -> Result { + let path = path.to_string(); + assert!( + std::path::Path::new(&path).exists(), + "model path file not exists" + ); + let tokenizer_file = path.clone() + "/tokenizer.json"; + assert!( + std::path::Path::new(&tokenizer_file).exists(), + "tokenizer.json not exists in model path" + ); + let tokenizer = Tokenizer::from_file(tokenizer_file) + .map_err(|e| anyhow!(format!("tokenizer from file error{}", e)))?; + Ok(Self { tokenizer }) + } + + pub fn text_encode(&self, text: String, device: &Device) -> Result { + let token_id = self + .tokenizer + .encode(text, true) + .map_err(|e| anyhow!(format!("tokenizer encode error: {}", e)))? + .get_ids() + .to_vec(); + let token_tensor = Tensor::from_slice(&token_id, (1, token_id.len()), device)?; + Ok(token_tensor) + } + + pub fn token_decode(&self, tokens: Vec) -> Result { + let decode = self + .tokenizer + .decode(&tokens, true) + .map_err(|e| anyhow!(format!("tokenizer encode error{}", e)))?; + Ok(decode) + } +} diff --git a/src/utils/audio_utils.rs b/src/utils/audio_utils.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/img_utils.rs b/src/utils/img_utils.rs new file mode 100644 index 0000000..84b438b --- /dev/null +++ b/src/utils/img_utils.rs @@ -0,0 +1,62 @@ +use std::io::Cursor; + +use anyhow::{Result, anyhow}; +use base64::{Engine, engine::general_purpose}; +use image::{DynamicImage, ImageReader}; + +pub fn load_image_from_url(url: &str) -> Result { + let response = reqwest::blocking::get(url) + .map_err(|e| anyhow!(format!("Failed to fetch image from url: {}", e)))?; + let bytes = response + .bytes() + .map_err(|e| anyhow!(format!("Failed to get image bytes: {}", e)))?; + + let cursor = Cursor::new(bytes); + let img = ImageReader::new(cursor) + .with_guessed_format() + .map_err(|e| anyhow!(format!("Failed to read image format: {}", e)))? + .decode() + .map_err(|e| anyhow!(format!("Failed to decode image: {}", e)))?; + Ok(img) +} + +pub fn load_image_from_base64(base64_data: &str) -> Result { + let image_data = general_purpose::STANDARD + .decode(base64_data) + .map_err(|e| anyhow!(format!("Failed to decode image: {}", e)))?; + let cursor = Cursor::new(image_data); + let img = ImageReader::new(cursor) + .with_guessed_format() + .map_err(|e| anyhow!(format!("Failed to read image format: {}", e)))? + .decode() + .map_err(|e| anyhow!(format!("Failed to decode image: {}", e)))?; + Ok(img) +} + +pub fn get_image(file: &String) -> Result { + let mut img = None; + if file.starts_with("http://") || file.starts_with("https://") { + img = Some(load_image_from_url(&file)?); + } + if file.starts_with("file://") { + let mut path = file.clone(); + path = path.split_off(7); + img = Some( + ImageReader::open(path) + .map_err(|e| anyhow!(format!("Failed to open file: {}", e)))? + .decode() + .map_err(|e| anyhow!(format!("Failed to decode image: {}", e)))?, + ); + } + if file.starts_with("data:image") { + if file.contains("base64,") { + let data: Vec<&str> = file.split("base64,").collect(); + let data = data[1]; + img = Some(load_image_from_base64(data)?); + } + } + if img.is_some() { + return Ok(img.unwrap()); + } + Err(anyhow!("get image from message failed".to_string())) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..d9f4429 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,4 @@ +pub mod tensor_utils; +pub mod utils; +pub mod img_utils; +pub mod video_utils; \ No newline at end of file diff --git a/src/utils/tensor_utils.rs b/src/utils/tensor_utils.rs new file mode 100644 index 0000000..9426025 --- /dev/null +++ b/src/utils/tensor_utils.rs @@ -0,0 +1,202 @@ +use anyhow::{Result, anyhow}; +use candle_core::{D, DType, Device, IndexOp, Tensor, shape::Dim}; + +pub fn split(t: &Tensor, splits: &[usize], dim: D) -> Result> { + let dim = dim.to_index(t.shape(), "split")?; + let mut split_res = Vec::new(); + let mut index = 0; + for split in splits { + split_res.push(t.narrow(dim, index, *split)?); + index += *split; + } + Ok(split_res) +} + +pub fn safe_arg_sort_last_dim(t: &Tensor, ascending: bool) -> Result { + // tensor在GPU上时,维度超过1024, arg_sort_last_dim方法会报错 + // 所以维度大于1024时,放到CPU上处理 + let last_dim = t.dims()[t.rank() - 1]; + if last_dim <= 1024 { + let t = t.arg_sort_last_dim(ascending)?; + Ok(t) + } else { + let cpu_tensor = t.to_device(&Device::Cpu)?; + let sorted_indices = cpu_tensor.arg_sort_last_dim(ascending)?; + let t = sorted_indices.to_device(t.device())?; + Ok(t) + } +} + +pub fn nonzero_index_vec(mask: &Tensor) -> Result> { + // 根据mask矩阵选出其中不为0的元素所在索引, 返回vec + // 只能处理1维数据 + let mut mask = mask.clone(); + if mask.dtype() != DType::U32 { + mask = mask.to_dtype(DType::U32)?; + } + match mask.rank() { + 0 => { + return Err(anyhow!(format!( + "input rank must > 0, the input tensor rank: {}", + mask.rank() + ))); + } + 1 => { + let mask_vector = mask.to_vec1::()?; + let indices: Vec = mask_vector + .iter() + .enumerate() + .filter_map(|(idx, &val)| if val != 0 { Some(idx as u32) } else { None }) + .collect(); + Ok(indices) + } + _ => { + return Err(anyhow!(format!( + "input rank not support, the input tensor rank: {}", + mask.rank() + ))); + } + } +} + +pub fn nonzero_index(mask: &Tensor) -> Result { + // 根据mask矩阵选出其中不为1的元素所在索引, 返回Tensor + let indices_tensor = match mask.rank() { + 0 => { + return Err(anyhow!(format!( + "input rank must > 0, the input tensor rank: {}", + mask.rank() + ))); + } + 1 => { + let index_vec = nonzero_index_vec(mask)?; + let indices_tensor = Tensor::from_slice(&index_vec, index_vec.len(), mask.device())?; + indices_tensor + } + _ => { + return Err(anyhow!(format!( + "input rank must == 1, the input tensor rank: {}", + mask.rank() + ))); + } + }; + Ok(indices_tensor) +} + +pub fn zero_index_vec(mask: &Tensor) -> Result> { + // 根据mask矩阵选出其中为0的元素所在索引, 返回vec + // 只能处理1维数据 + let mut mask = mask.clone(); + if mask.dtype() != DType::U32 { + mask = mask.to_dtype(DType::U32)?; + } + match mask.rank() { + 0 => { + return Err(anyhow!(format!( + "input rank must > 0, the input tensor rank: {}", + mask.rank() + ))); + } + 1 => { + let mask_vector = mask.to_vec1::()?; + let indices: Vec = mask_vector + .iter() + .enumerate() + .filter_map(|(idx, &val)| if val == 0 { Some(idx as u32) } else { None }) + .collect(); + Ok(indices) + } + _ => { + return Err(anyhow!(format!( + "input rank not support, the input tensor rank: {}", + mask.rank() + ))); + } + } +} + +pub fn zero_index(mask: &Tensor) -> Result { + let index_vec = zero_index_vec(mask)?; + let indices_tensor = Tensor::from_slice(&index_vec, index_vec.len(), mask.device())?; + Ok(indices_tensor) +} + +pub fn nonzero_slice(mask: &Tensor) -> Result> { + // 根据mask矩阵选出其中非0的元素所在索引 + // 根据索引获取连续索引间隔 + // 如不为零索引元素为[0, 3, 4, 5, 8, 9] + // 间隔为: [(0, 1), (3, 6), (8, 10)] + // 索引前闭后开 + let mut index_vec = nonzero_index_vec(mask)?; + match index_vec.len() { + 0 => { + return Ok(vec![]); + } + 1 => { + return Ok(vec![(index_vec[0] as usize, (index_vec[0] + 1) as usize)]); + } + _ => { + let mut vec_slice = vec![]; + let mut start = index_vec.remove(0); + let mut last = start; + + for i in index_vec { + if i == (last + 1) { + last = i; + continue; + } else { + vec_slice.push((start as usize, (last + 1) as usize)); + start = i; + last = i; + } + } + vec_slice.push((start as usize, (last + 1) as usize)); + Ok(vec_slice) + } + } +} + +pub fn masked_scatter_dim0(original: &Tensor, replace: &Tensor, mask: &Tensor) -> Result { + // 根据mask中非0元素所在索引,使用replace中的数据替换掉original中的数据 + // original: rank = 3: (bs, seq_len, hidden_dim) + // replace: rank = 2: (seq_len, hidden_dim) + // mask: rank = 2: (bs, seq_len) + // 推理时bs=1,为了方便替换,将bs squeeze,替换后再unsqueeze + // 按行替换 + if original.dim(0)? != 1 || mask.dim(0)? != 1 { + return Err(anyhow!(format!( + "masked_scatter_dim0 original bs: {} or mask bs :{} not equal to 1 ", + original.dim(0)?, + mask.dim(0)? != 1 + ))); + } + let mut original = original.squeeze(0)?; + let mask = mask.squeeze(0)?; + let slices = nonzero_slice(&mask)?; + let mut sub_start = 0usize; + let mut sub_end = 0usize; + for (start, end) in slices { + sub_end = sub_start + (end - start); + let sub_replace = replace.i((sub_start..sub_end, ..))?; + original = original.slice_assign(&[(start..end), (0..original.dim(1)?)], &sub_replace)?; + sub_start = sub_end; + } + original = original.unsqueeze(0)?; + Ok(original) +} + +pub fn get_equal_mask(input_ids: &Tensor, token_ids: u32) -> Result { + let image_token_id_tensor = Tensor::new(vec![token_ids], input_ids.device())?; + let mask = input_ids + .broadcast_eq(&image_token_id_tensor)? + .to_dtype(candle_core::DType::U32)?; + Ok(mask) +} + +pub fn get_vision_next_indices(input_ids: &Tensor, token_id: u32) -> Result { + // input_ids -> shape: (seq_len) + let mask = get_equal_mask(&input_ids, token_id)?; + let indices = nonzero_index(&mask)?; + let indices = indices.broadcast_add(&Tensor::new(vec![1u32], input_ids.device())?)?; + Ok(indices) +} diff --git a/src/utils/utils.rs b/src/utils/utils.rs new file mode 100644 index 0000000..fd26e1d --- /dev/null +++ b/src/utils/utils.rs @@ -0,0 +1,87 @@ +use anyhow::Result; +use candle_core::{DType, Device}; + +pub fn get_device(device: Option<&Device>) -> Device { + match device { + Some(d) => d.clone(), + None => { + #[cfg(feature = "cuda")] + { + Device::new_cuda(0).unwrap_or(Device::Cpu) + } + #[cfg(not(feature = "cuda"))] + { + Device::Cpu + } + } + } +} + +pub fn get_dtype(dtype: Option, cfg_dtype: &str) -> DType { + match dtype { + Some(d) => d, + None => { + #[cfg(feature = "cuda")] + { + match cfg_dtype { + "float32" | "float" => DType::F32, + "float64" | "double" => DType::F64, + "float16" => DType::F16, + "bfloat16" => DType::BF16, + "uint8" => DType::U8, + "int8" | "int16" | "int32" | "int64" => DType::I64, + _ => DType::F32, + } + } + #[cfg(not(feature = "cuda"))] + { + match cfg_dtype { + "float32" | "float" => DType::F32, + "float64" | "double" => DType::F64, + "float16" | "bfloat16" => DType::F16, // cpu上bfloat16有问题 + "uint8" => DType::U8, + "int8" | "int16" | "int32" | "int64" => DType::I64, + _ => DType::F32, + } + } + } + } +} + +pub fn string_to_static_str(s: String) -> &'static str { + Box::leak(s.into_boxed_str()) +} + +pub fn find_safetensors_files(path: &str) -> Result> { + let mut files = Vec::new(); + + for entry in std::fs::read_dir(path)? { + let entry = entry?; + let file_path = entry.path(); + + if file_path.is_file() { + if let Some(extension) = file_path.extension() { + if extension == "safetensors" { + files.push(file_path.to_string_lossy().to_string()); + } + } + } + } + + Ok(files) +} + +pub fn round_by_factor(num: u32, factor: u32) -> u32 { + let round = (num as f32 / factor as f32).round() as u32; + round * factor +} + +pub fn floor_by_factor(num: f32, factor: u32) -> u32 { + let floor = (num / factor as f32).floor() as u32; + floor * factor +} + +pub fn ceil_by_factor(num: f32, factor: u32) -> u32 { + let ceil = (num / factor as f32).ceil() as u32; + ceil * factor +} diff --git a/src/utils/video_utils.rs b/src/utils/video_utils.rs new file mode 100644 index 0000000..417928c --- /dev/null +++ b/src/utils/video_utils.rs @@ -0,0 +1,13 @@ +use ffmpeg_next as ffmpeg; +use std::{fs::File, io::Write}; + +#[allow(unused)] +fn save_file( + frame: &ffmpeg::frame::Video, + index: usize, +) -> std::result::Result<(), std::io::Error> { + let mut file = File::create(format!("frame{}.ppm", index))?; + file.write_all(format!("P6\n{} {}\n255\n", frame.width(), frame.height()).as_bytes())?; + file.write_all(frame.data(0))?; + Ok(()) +} diff --git a/tests/config_tests.rs b/tests/config_tests.rs new file mode 100644 index 0000000..8e41bf1 --- /dev/null +++ b/tests/config_tests.rs @@ -0,0 +1,12 @@ +use anyhow::Result; +use aha::models::qwen2_5vl::config::Config; + +#[test] +fn qwen2_5vl_config() -> Result<()> { + // cargo test qwen2_5vl_config -- --nocapture + let model_path = "/home/jhq/huggingface_model/Qwen/Qwen2.5-VL-3B-Instruct/"; + let config_path = model_path.to_string() + "/config.json"; + let config: Config = serde_json::from_slice(&std::fs::read(config_path)?)?; + println!("{:?}", config); + Ok(()) +} \ No newline at end of file diff --git a/tests/test_qwen2_5vl.rs b/tests/test_qwen2_5vl.rs new file mode 100644 index 0000000..3c5bcd5 --- /dev/null +++ b/tests/test_qwen2_5vl.rs @@ -0,0 +1,56 @@ +use std::time::Instant; + +use aha::{models::{qwen2_5vl::generate::Qwen2_5VLGenerateModel, GenerateModel}, ModelType}; +use anyhow::{Result}; +use candle_core::{DType, Device}; +use openai_dive::v1::resources::chat::ChatCompletionParameters; + + +#[test] +fn qwen2_5vl_generate() -> Result<()> { + // test with cpu :(太慢了, : RUST_BACKTRACE=1 cargo test qwen2_5vl_generate -- --nocapture + // test with cuda: RUST_BACKTRACE=1 cargo test -F cuda qwen2_5vl_generate -- --nocapture + // test with cuda+flash-attn: RUST_BACKTRACE=1 cargo test -F cuda,flash-attn qwen2_5vl_generate -- --nocapture + let device = Device::cuda_if_available(0)?; + let dtype = DType::BF16; + + let model_path = "/home/jhq/huggingface_model/Qwen/Qwen2.5-VL-3B-Instruct/"; + + let message = r#" + { + "model": "qwen2.5vl", + "messages": [ + { + "role": "user", + "content": [ + { + "type": "image", + "image_url": + { + "url": "file://./assets/img/ocr_test.png" + } + }, + { + "type": "text", + "text": "请分析图片并提取所有可见文本内容,按从左到右、从上到下的布局,返回纯文本" + } + ] + } + ] + } + "#; + let mes:ChatCompletionParameters = serde_json::from_str(message)?; + let i_start = Instant::now(); + // let mut model = Qwen2_5VLGenerateModel::init(model_path, &device, dtype)?; + let mut model = ModelType::init(ModelType::Qwen2_5VL, model_path, None, None)?; + let i_duration = i_start.elapsed(); + println!("Time elapsed in load model is: {:?}", i_duration); + + let i_start = Instant::now(); + let result = model.generate(mes)?; + println!("generate: \n{}", result); + let i_duration = i_start.elapsed(); + println!("Time elapsed in generate is: {:?}", i_duration); + + Ok(()) +} \ No newline at end of file