diff --git a/Cargo.lock b/Cargo.lock index 8078d24f..65b9f8b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "anstream" version = "0.6.13" @@ -94,12 +100,35 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[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 2.0.55", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-rustls" version = "0.3.0" @@ -150,6 +179,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + [[package]] name = "average" version = "0.14.2" @@ -161,6 +204,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "awaitdrop" version = "0.1.2" @@ -267,6 +319,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "bit-set" version = "0.5.3" @@ -282,6 +340,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -294,6 +358,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bitstream-io" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da" + [[package]] name = "block-buffer" version = "0.10.4" @@ -303,6 +373,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "built" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d17f4d6e4dc36d1a02fbedc2753a096848e7c1b0772f7654eab8e2c927dd53" + [[package]] name = "bumpalo" version = "3.15.4" @@ -315,6 +391,12 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" + [[package]] name = "byteorder" version = "1.5.0" @@ -370,6 +452,20 @@ name = "cc" version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-expr" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +dependencies = [ + "smallvec", + "target-lexicon", +] [[package]] name = "cfg-if" @@ -423,6 +519,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.0" @@ -535,6 +637,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -736,6 +844,22 @@ dependencies = [ "cc", ] +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fancy-regex" version = "0.11.0" @@ -752,6 +876,15 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -780,6 +913,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28a80e3145d8ad11ba0995949bbcf48b9df2be62772b3d351ef017dff6ecb853" +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -941,6 +1083,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.28.1" @@ -976,6 +1128,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1161,6 +1323,45 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a84a25dcae3ac487bc24ef280f9e20c79c9b1a3e5e32cbed3041d1c514aa87c" +dependencies = [ + "byteorder", + "thiserror", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "1.9.3" @@ -1223,6 +1424,17 @@ dependencies = [ "cfg-if", ] +[[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 2.0.55", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -1271,6 +1483,21 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.69" @@ -1316,12 +1543,29 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libm" version = "0.2.8" @@ -1361,6 +1605,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "mach2" version = "0.4.2" @@ -1407,6 +1660,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[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.2" @@ -1486,8 +1749,8 @@ dependencies = [ [[package]] name = "minijinja" -version = "1.0.16" -source = "git+https://github.com/mitsuhiko/minijinja.git?branch=main#82d0160b5513844e5429db084f1cbdd3313ed482" +version = "1.0.12" +source = "git+https://github.com/mitsuhiko/minijinja.git?rev=5cd4efb#5cd4efb9e2639247df275fe6e22a5dbe0ce71b28" dependencies = [ "serde", ] @@ -1505,6 +1768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", + "simd-adler32", ] [[package]] @@ -1583,6 +1847,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "ngrok" version = "0.13.1" @@ -1642,6 +1912,12 @@ dependencies = [ "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 = "ntapi" version = "0.4.1" @@ -1707,6 +1983,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[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 2.0.55", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2071,6 +2358,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "portable-atomic" version = "1.6.0" @@ -2132,6 +2432,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.55", +] + [[package]] name = "prost" version = "0.11.9" @@ -2209,6 +2528,15 @@ dependencies = [ "prost 0.12.3", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quanta" version = "0.11.1" @@ -2225,6 +2553,12 @@ dependencies = [ "winapi", ] +[[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.35" @@ -2281,6 +2615,56 @@ dependencies = [ "unicode-width", ] +[[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", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-cpuid" version = "10.7.0" @@ -2431,6 +2815,15 @@ dependencies = [ "winreg", ] +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.16.20" @@ -2695,6 +3088,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2766,6 +3168,21 @@ dependencies = [ "libc", ] +[[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 = "sketches-ddsketch" version = "0.2.2" @@ -2817,6 +3234,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spm_precompiled" @@ -2933,6 +3353,19 @@ dependencies = [ "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 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + [[package]] name = "tabled" version = "0.14.0" @@ -2957,6 +3390,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + [[package]] name = "tempfile" version = "3.10.1" @@ -3029,10 +3468,12 @@ dependencies = [ "async-stream", "axum", "axum-tracing-opentelemetry", + "base64 0.22.0", "clap", "futures", "futures-util", "hf-hub", + "image", "init-tracing-opentelemetry", "jsonschema", "metrics", @@ -3092,6 +3533,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.34" @@ -3295,6 +3747,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.9.2" @@ -3699,6 +4185,17 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.0" @@ -3727,6 +4224,12 @@ dependencies = [ "time", ] +[[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.4" @@ -3853,6 +4356,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "which" version = "4.4.2" @@ -4113,6 +4622,15 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -4160,3 +4678,27 @@ dependencies = [ "crossbeam-utils", "flate2", ] + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/router/Cargo.toml b/router/Cargo.toml index 0c57a886..582bbdfb 100644 --- a/router/Cargo.toml +++ b/router/Cargo.toml @@ -44,10 +44,12 @@ utoipa = { version = "3.5.0", features = ["axum_extras"] } utoipa-swagger-ui = { version = "3.1.5", features = ["axum"] } ngrok = { version = "0.13.1", features = ["axum"], optional = true } init-tracing-opentelemetry = { version = "0.14.1", features = ["opentelemetry-otlp"] } -minijinja = { git = "https://github.com/mitsuhiko/minijinja.git", branch = "main", commit = "5cd4efb" } +minijinja = { git = "https://github.com/mitsuhiko/minijinja.git", rev = "5cd4efb" } futures-util = "0.3.30" regex = "1.10.3" once_cell = "1.19.0" +image = "0.25.1" +base64 = "0.22.0" [build-dependencies] vergen = { version = "8.2.5", features = ["build", "git", "gitcl"] } diff --git a/router/client/src/client.rs b/router/client/src/client.rs index 429da71a..ae926139 100644 --- a/router/client/src/client.rs +++ b/router/client/src/client.rs @@ -112,8 +112,11 @@ impl Client { // Create requests while n_tokens < max_prefill_tokens { let truncate = min(max_input_length, max_prefill_tokens - n_tokens); - let mut inputs = "_test ".to_string().repeat(max_input_length as usize); + + let mut inputs = String::new(); inputs.push_str("![](data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAABg2lDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV/TSotUROxQxCFDdbKLijjWKhShQqgVWnUwufQLmrQkKS6OgmvBwY/FqoOLs64OroIg+AHi7OCk6CIl/i8ptIjx4Lgf7+497t4BQqvKNDOQADTdMjKppJjLr4rBVwQQwhAERGVm1uckKQ3P8XUPH1/v4jzL+9yfY0AtmAzwicQJVjcs4g3imU2rznmfOMLKskp8Tjxh0AWJH7muuPzGueSwwDMjRjYzTxwhFks9rPQwKxsa8TRxTNV0yhdyLquctzhr1Qbr3JO/MFzQV5a5TnMUKSxiCRJEKGiggiosxGnVSTGRof2kh3/E8UvkUshVASPHAmrQIDt+8D/43a1ZnJp0k8JJoO/Ftj/GgOAu0G7a9vexbbdPAP8zcKV3/bUWMPtJerOrxY6AwW3g4rqrKXvA5Q4QfarLhuxIfppCsQi8n9E35YHhW6B/ze2ts4/TByBLXaVvgINDYLxE2ese7w719vbvmU5/PycecohsjayNAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH6AQIEQMnlTSSjwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAASSURBVDjLY2AYBaNgFIyCoQsABMQAAeRw1DoAAAAASUVORK5CYII="); + inputs.push_str(&"_test ".to_string().repeat(max_input_length as usize)); + requests.push(Request { id: 0, // We truncate the input on the server side to be sure that it has the correct size diff --git a/router/src/config.rs b/router/src/config.rs new file mode 100644 index 00000000..64663a7a --- /dev/null +++ b/router/src/config.rs @@ -0,0 +1,99 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "model_type")] +#[serde(rename_all = "snake_case")] +pub struct LlavaNext { + text_config: Box, + vision_config: VisionConfig, + image_grid_pinpoints: Vec<(usize, usize)>, +} + +fn get_anyres_image_grid_shape( + height: usize, + width: usize, + grid_pinpoints: &[(usize, usize)], + patch_size: usize, +) -> (usize, usize) { + let (height, width) = select_best_resolution(height, width, grid_pinpoints); + (height / patch_size, width / patch_size) +} + +/// Selects the best resolution from a list of possible resolutions based on the original size. +/// This is done by calculating the effective and wasted resolution for each possible resolution. +/// The best fit resolution is the one that maximizes the effective resolution and minimizes the wasted resolution. +fn select_best_resolution( + original_height: usize, + original_width: usize, + possible_resolutions: &[(usize, usize)], +) -> (usize, usize) { + let mut best_fit = None; + let mut max_effective_resolution = 0; + let mut min_wasted_resolution = f32::NEG_INFINITY; + + for (height, width) in possible_resolutions { + // let scale = std::cmp::min(width / original_width, height / original_height); + let downscaled_width = width / original_width * original_width; + let downscaled_height = height / original_height * original_height; + let effective_resolution = std::cmp::min( + downscaled_width * downscaled_height, + original_width * original_height, + ); + let wasted_resolution = (width * height) - effective_resolution; + + if effective_resolution > max_effective_resolution + || (effective_resolution == max_effective_resolution + && (wasted_resolution as f32) < min_wasted_resolution) + { + max_effective_resolution = effective_resolution; + min_wasted_resolution = wasted_resolution as f32; + best_fit = Some((*height, *width)); + } + } + + best_fit.expect("Expect a resolution to exist") +} + +impl LlavaNext { + pub fn get_number_of_features(&self, height: usize, width: usize) -> usize { + let image_size = self.vision_config.image_size; + let patch_size = self.vision_config.patch_size; + assert!(image_size % patch_size == 0); + let npatches = image_size / patch_size; + let (num_patch_height, num_patch_width) = + get_anyres_image_grid_shape(height, width, &self.image_grid_pinpoints, image_size); + // Ceil + let height_of_patch = (height * npatches + width - 1) / width; + let unpadded_features = npatches * height_of_patch * num_patch_height * num_patch_width; + // They are only added after width + let newline_features = height_of_patch * num_patch_width; + // The base patch covers the entire image + let base_features = npatches.pow(2); + unpadded_features + newline_features + base_features + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "model_type")] +#[serde(rename_all = "snake_case")] +pub struct ClipVisionModel { + image_size: usize, + patch_size: usize, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "model_type")] +#[serde(rename_all = "snake_case")] +pub enum Config { + LlavaNext(LlavaNext), + ClipVisionModel(ClipVisionModel), + Mistral, + Idefics, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct VisionConfig { + image_size: usize, + patch_size: usize, +} diff --git a/router/src/lib.rs b/router/src/lib.rs index 5415a956..c787470b 100644 --- a/router/src/lib.rs +++ b/router/src/lib.rs @@ -1,3 +1,4 @@ +pub mod config; mod health; /// Text Generation Inference Webserver mod infer; diff --git a/router/src/main.rs b/router/src/main.rs index 6a736b12..f180d65b 100644 --- a/router/src/main.rs +++ b/router/src/main.rs @@ -13,6 +13,7 @@ use std::io::BufReader; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::Path; use text_generation_client::{ClientError, ShardedClient}; +use text_generation_router::config::Config; use text_generation_router::{server, HubModelInfo, HubTokenizerConfig}; use thiserror::Error; use tokenizers::Tokenizer; @@ -191,15 +192,19 @@ async fn main() -> Result<(), RouterError> { }; // Load tokenizer and model info - let (tokenizer, model_info) = if local_model { + let (tokenizer, model_info, config) = if local_model { let tokenizer = Tokenizer::from_file(local_path.join("tokenizer.json")).ok(); let model_info = HubModelInfo { model_id: tokenizer_name.to_string(), sha: None, pipeline_tag: None, }; + let config: Option = std::fs::read_to_string(local_path.join("config.json")) + .ok() + .as_ref() + .and_then(|c| serde_json::from_str(c).ok()); - (tokenizer, model_info) + (tokenizer, model_info, config) } else if let Some(api) = api.clone() { let api_repo = api.repo(Repo::with_revision( tokenizer_name.to_string(), @@ -212,6 +217,18 @@ async fn main() -> Result<(), RouterError> { Err(_) => get_base_tokenizer(&api, &api_repo).await, }; + let config: Option = api_repo.get("config.json").await.ok().and_then(|filename| { + tracing::info!("Config filename {filename:?}"); + std::fs::read_to_string(filename) + .ok() + .as_ref() + .and_then(|c| { + let config: Result = serde_json::from_str(c); + tracing::info!("Config parse {config:?}"); + config.ok() + }) + }); + let model_info = get_model_info(&api_repo).await.unwrap_or_else(|| { tracing::warn!("Could not retrieve model info from the Hugging Face hub."); HubModelInfo { @@ -221,7 +238,7 @@ async fn main() -> Result<(), RouterError> { } }); - (tokenizer, model_info) + (tokenizer, model_info, config) } else { // No API and no local model return Err(RouterError::ArgumentValidation( @@ -229,6 +246,8 @@ async fn main() -> Result<(), RouterError> { )); }; + tracing::info!("Using config {config:?}"); + // Load tokenizer config if found locally, or check if we can get it from the API if needed let tokenizer_config = if let Some(path) = tokenizer_config_path { tracing::info!("Using local tokenizer config from user specified path"); @@ -363,6 +382,7 @@ async fn main() -> Result<(), RouterError> { max_batch_size, sharded_client, tokenizer, + config, validation_workers, addr, cors_allow_origin, diff --git a/router/src/server.rs b/router/src/server.rs index c698c4f8..b8f93514 100644 --- a/router/src/server.rs +++ b/router/src/server.rs @@ -1,3 +1,4 @@ +use crate::config::Config; /// HTTP Server logic use crate::health::Health; use crate::infer::{InferError, InferResponse, InferStreamResponse}; @@ -1155,6 +1156,7 @@ pub async fn run( max_batch_size: Option, client: ShardedClient, tokenizer: Option, + config: Option, validation_workers: usize, addr: SocketAddr, allow_origin: Option, @@ -1237,6 +1239,7 @@ pub async fn run( let validation = Validation::new( validation_workers, tokenizer, + config, max_best_of, max_stop_sequences, max_top_n_tokens, diff --git a/router/src/validation.rs b/router/src/validation.rs index 3f1b1114..646ff229 100644 --- a/router/src/validation.rs +++ b/router/src/validation.rs @@ -1,15 +1,19 @@ +use crate::config::Config; /// Payload validation logic use crate::validation::ValidationError::{BestOfSampling, BestOfSeed, EmptyInput}; use crate::{GenerateParameters, GenerateRequest, GrammarType}; use jsonschema::{Draft, JSONSchema}; use rand::{thread_rng, Rng}; use serde_json::Value; +use std::io::Cursor; use text_generation_client::{ GrammarType as ProtoGrammarType, NextTokenChooserParameters, StoppingCriteriaParameters, }; use thiserror::Error; use tokenizers::tokenizer::Tokenizer; -use tokenizers::TruncationDirection; +// use tokenizers::TruncationDirection; +use base64::{engine::general_purpose::STANDARD, Engine}; +use image::{io::Reader as ImageReader, ImageFormat}; use tokio::sync::mpsc; use tokio::sync::oneshot; use tracing::{instrument, Span}; @@ -34,6 +38,7 @@ impl Validation { pub(crate) fn new( workers: usize, tokenizer: Option, + config: Option, max_best_of: usize, max_stop_sequences: usize, max_top_n_tokens: u32, @@ -50,12 +55,13 @@ impl Validation { // Create workers for _ in 0..workers { let tokenizer_clone = tokenizer.clone(); + let config_clone = config.clone(); let (tokenizer_sender, tokenizer_receiver) = mpsc::unbounded_channel(); senders.push(tokenizer_sender); // Spawn worker tokio::task::spawn_blocking(move || { - tokenizer_worker(tokenizer_clone, tokenizer_receiver) + tokenizer_worker(tokenizer_clone, config_clone, tokenizer_receiver) }); } @@ -408,54 +414,137 @@ async fn round_robin_task( } /// Start tokenization workers -fn tokenizer_worker(tokenizer: Tokenizer, mut receiver: mpsc::UnboundedReceiver) { +fn tokenizer_worker( + tokenizer: Tokenizer, + config: Option, + mut receiver: mpsc::UnboundedReceiver, +) { // Loop over requests - let is_multimodal = { - let vocab = tokenizer.get_vocab(true); - vocab.contains_key("") - }; while let Some(((inputs, truncate), response_tx, parent_span)) = receiver.blocking_recv() { parent_span.in_scope(|| { response_tx - .send(prepare_input(inputs, truncate, &tokenizer, is_multimodal)) + .send(prepare_input(inputs, truncate, &tokenizer, &config)) .unwrap_or(()) }) } } +fn format_from_mimetype(mimetype: &str) -> Option { + match mimetype { + "image/png" => Some(ImageFormat::Png), + "image/jpeg" => Some(ImageFormat::Jpeg), + "image/jpg" => Some(ImageFormat::Jpeg), + "image/gif" => Some(ImageFormat::Gif), + "image/webp" => Some(ImageFormat::WebP), + "image/tiff" => Some(ImageFormat::Tiff), + // "image/pnm"=>Some(ImageFormat::Pnm), + // "image/tga"=>Some(ImageFormat::Tga), + // "image/dds"=>Some(ImageFormat::Dds), + // "image/bmp"=>Some(ImageFormat::Bmp), + // "image/ico"=>Some(ImageFormat::Ico), + // "image/x-exr"=>Some(ImageFormat::OpenExr), + _ => None, + } +} +fn format_to_mimetype(format: ImageFormat) -> String { + match format { + ImageFormat::Png => "image/png", + ImageFormat::Jpeg => "image/jpeg", + ImageFormat::Gif => "image/gif", + ImageFormat::WebP => "image/webp", + ImageFormat::Tiff => "image/tiff", + _ => "application/octet-stream", + } + .to_string() +} + +fn fetch_image(input: &str) -> Result<(String, usize, usize), ValidationError> { + if input.starts_with("![](http://") || input.starts_with("![](https://") { + let url = &input["![](".len()..input.len() - 1]; + let data = reqwest::blocking::get(url)?.bytes()?; + + let format = image::guess_format(&data)?; + // TODO Remove this clone + let img = ImageReader::with_format(Cursor::new(data.clone()), format).decode()?; + let height: usize = img.height().try_into()?; + let width: usize = img.width().try_into()?; + let mimetype = format_to_mimetype(format); + let encoded = STANDARD.encode(data); + let data_uri = format!("![](data:{mimetype};base64,{encoded})"); + Ok((data_uri, height, width)) + } else if input.starts_with("![](data:") { + // Remove ![](....) + let content = &input["![](data:".len()..input.len() - 1]; + let tokens: Vec<_> = content.split(';').collect(); + if tokens.len() != 2 { + return Err(ValidationError::InvalidImageContent(content.to_string())); + } + let mimetype = tokens[0]; + let content = tokens[1]; + + if !content.starts_with("base64,") { + return Err(ValidationError::InvalidImageContent(content.to_string())); + } + + let data = STANDARD.decode(content["base64,".len()..].as_bytes())?; + let img = if let Some(format) = format_from_mimetype(mimetype) { + ImageReader::with_format(Cursor::new(data), format).decode()? + } else { + ImageReader::new(Cursor::new(data)) + .with_guessed_format() + .map_err(|_io_error| ValidationError::InvalidImageContent(content.to_string()))? + .decode()? + }; + + let height: usize = img.height().try_into()?; + let width: usize = img.width().try_into()?; + Ok((input.to_string(), height, width)) + } else { + Err(ValidationError::InvalidImageContent(input.to_string())) + } +} + /// Get input length and optionally truncate it fn prepare_input( mut inputs: String, - truncate: Option, + _truncate: Option, tokenizer: &Tokenizer, - is_multimodal: bool, + config: &Option, ) -> Result<(tokenizers::Encoding, String), ValidationError> { - let simplified_query = if is_multimodal { - static RE: Lazy = Lazy::new(|| Regex::new(r"!\[\]\([^\)]*\)").unwrap()); - // HACK: Llava uses arbitrary number of tokens (between 576 and ~576 * 5). - // Idefics uses a sequence encoder which doesn't take as much space in the KV cache, but - // there is still some encoder values. - // This hacks just forces more "allocation" for the given - RE.replace_all(&inputs, "".repeat(576)).into() - } else { - inputs.clone() - }; - // Get the number of tokens in the input - let mut encoding = tokenizer - .encode(simplified_query, true) - .map_err(|err| ValidationError::Tokenizer(err.to_string()))?; - - // Optionally truncate - if let Some(truncate) = truncate { - // XXX: Critical to keep the multimodal check otherwise this modifies the original string - // Which we really don't want. - if truncate < encoding.len() && !is_multimodal { - encoding.truncate(truncate, 0, TruncationDirection::Left); - inputs = tokenizer - .decode(encoding.get_ids(), false) - .map_err(|err| ValidationError::Tokenizer(err.to_string()))?; + static RE: Lazy = Lazy::new(|| Regex::new(r"!\[\]\([^\)]*\)").unwrap()); + let tokenizer_query = match config { + Some(Config::LlavaNext(config)) => { + let mut modified_inputs = String::with_capacity(inputs.len()); + let mut tokenizer_query = String::with_capacity(inputs.len()); + let mut start = 0; + for chunk in RE.find_iter(&inputs) { + let chunk_start = chunk.start(); + let chunk_end = chunk.end(); + if chunk_start != start { + modified_inputs.push_str(&inputs[start..chunk_start]); + tokenizer_query.push_str(&inputs[start..chunk_start]); + } + let (image_uri, height, width) = fetch_image(&inputs[chunk_start..chunk_end])?; + let slots = config.get_number_of_features(height, width); + tokenizer_query.push_str(&"".repeat(slots)); + modified_inputs.push_str(&image_uri); + start = chunk_end; + } + if start != inputs.len() - 1 { + modified_inputs.push_str(&inputs[start..]); + tokenizer_query.push_str(&inputs[start..]); + } + inputs = modified_inputs; + tokenizer_query } - } + Some(Config::Idefics) => RE.replace_all(&inputs, "").into(), + _ => inputs.clone(), + }; + + // Get the number of tokens in the input + let encoding = tokenizer + .encode(tokenizer_query, true) + .map_err(|err| ValidationError::Tokenizer(err.to_string()))?; Ok((encoding, inputs)) } @@ -529,6 +618,16 @@ pub enum ValidationError { Grammar, #[error("grammar is not valid: {0}")] InvalidGrammar(String), + #[error("base64 encoding is invalid: {0}")] + InvalidBase64(#[from] base64::DecodeError), + #[error("invalid image: {0}")] + InvalidImage(#[from] image::ImageError), + #[error("invalid integer: {0}")] + InvalidInt(#[from] core::num::TryFromIntError), + #[error("invalid image content: {0}")] + InvalidImageContent(String), + #[error("Could not fetch image: {0}")] + FailedFetchImage(#[from] reqwest::Error), } #[cfg(test)] diff --git a/server/text_generation_server/models/vlm_causal_lm.py b/server/text_generation_server/models/vlm_causal_lm.py index c965bea8..8a647a09 100644 --- a/server/text_generation_server/models/vlm_causal_lm.py +++ b/server/text_generation_server/models/vlm_causal_lm.py @@ -90,9 +90,6 @@ def get_number_of_features(height: int, width: int, config) -> int: # The base patch covers the entire image base_features = npatches**2 return unpadded_features + newline_features + base_features - if height == 640 and width == 640: - return 2928 - return 2634 def load_data_uri(image_uri: str) -> Image.Image: @@ -138,13 +135,16 @@ class VlmCausalLMBatch(FlashMistralBatch): full_text += chunk["content"] elif chunk["type"] == "image": image = chunk["content"] - if image.startswith("https://") or image.startswith("http://"): - image = processor.image_processor.fetch_images(image) - elif image.startswith("data:"): + # Should never receive URLs anymore, processing should be done + # On the rust layer. + # This avoid making n queries per TP + # if image.startswith("https://") or image.startswith("http://"): + # image = processor.image_processor.fetch_images(image) + if image.startswith("data:"): image = load_data_uri(image) else: raise RuntimeError( - "Cannot process input image not starting with http(s):// nor data:" + "Cannot process input image not starting with data:" ) image_input = processor.image_processor(image, return_tensors="pt") height, width = image_input["image_sizes"][0]