diff --git a/gatewaybench/.gitignore b/gatewaybench/.gitignore new file mode 100644 index 00000000000..6c4b5920723 --- /dev/null +++ b/gatewaybench/.gitignore @@ -0,0 +1,2 @@ +/target +results/*.jsonl diff --git a/gatewaybench/Cargo.lock b/gatewaybench/Cargo.lock new file mode 100644 index 00000000000..ecdb1d8bfaa --- /dev/null +++ b/gatewaybench/Cargo.lock @@ -0,0 +1,1921 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-lc-rs" +version = "1.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[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-channel" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "driver" +version = "0.1.0" +dependencies = [ + "gwbench-core", + "hdrhistogram", + "reqwest", + "tokio", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[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 = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" + +[[package]] +name = "futures-sink" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" + +[[package]] +name = "futures-task" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" + +[[package]] +name = "futures-util" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "rand_core", + "wasm-bindgen", +] + +[[package]] +name = "gwbench-core" +version = "0.1.0" +dependencies = [ + "hdrhistogram", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hdrhistogram" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d1053f4708f0af3cf9fc5bffc7e68a914a3c45becb231c80068c9c3f78bea" +dependencies = [ + "base64", + "byteorder", + "crossbeam-channel", + "flate2", + "nom", + "num-traits", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "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.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mock-upstream" +version = "0.1.0" +dependencies = [ + "axum", + "serde_json", + "tokio", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.23.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967" +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 2.0.119", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "test-chunk-fidelity" +version = "0.1.0" + +[[package]] +name = "test-inter-chunk-latency" +version = "0.1.0" + +[[package]] +name = "test-large-prompt" +version = "0.1.0" + +[[package]] +name = "test-tail-latency" +version = "0.1.0" + +[[package]] +name = "test-throughput" +version = "0.1.0" + +[[package]] +name = "test-tool-call-latency" +version = "0.1.0" + +[[package]] +name = "test-ttft" +version = "0.1.0" + +[[package]] +name = "thiserror" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.0", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +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 = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +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 = "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 = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "xtask" +version = "0.1.0" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/gatewaybench/Cargo.toml b/gatewaybench/Cargo.toml new file mode 100644 index 00000000000..4e8c6956316 --- /dev/null +++ b/gatewaybench/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "2" +members = ["crates/*", "tests/*", "xtask"] diff --git a/gatewaybench/README.md b/gatewaybench/README.md new file mode 100644 index 00000000000..5fdcd22ad11 --- /dev/null +++ b/gatewaybench/README.md @@ -0,0 +1,136 @@ +# GatewayBench + +A reproducible benchmark for **AI-gateway overhead**: the latency, throughput ceiling, and resource cost a gateway adds *on top of* the upstream LLM. + +CursorBench measures model *quality*. The right question for a gateway is not quality but overhead, and specifically the overhead that a coding agent feels on every turn: how much longer until the first token, how evenly the stream flows, how tool calls behave, and how the whole thing holds up under concurrency. GatewayBench measures that, and it measures it the same way for every gateway so the numbers are comparable. + +![GatewayBench throughput vs memory](analyze/throughput_vs_memory.png) + +![GatewayBench streaming TTFT overhead vs memory](analyze/ttft_vs_memory.png) + +> The charts above use **illustrative placeholder data** to show the design. They are regenerated from real `results/*.jsonl` once the harness has run; see [Generating the charts](#generating-the-charts). + +## The core idea: isolate the gateway from the provider + +Real provider latency is hundreds of milliseconds to several seconds with large variance. Gateway overhead is microseconds to low-single-digit milliseconds. Benchmarking against a live provider drowns the signal in provider noise and nothing reproduces. + +So every self-hostable gateway points at a **local mock upstream** we control (`crates/mock-upstream`): an OpenAI-compatible server with deterministic, configurable behavior + +- fixed, configurable time-to-first-byte and inter-token delay, so streaming is realistic without real-world variance +- configurable request size (prompt tokens) and response size (completion tokens) +- correct SSE streaming for `stream: true`, a normal JSON body otherwise +- a valid `usage` block, so spend-tracking and logging code paths actually run + +Overhead is then + +``` +overhead = latency(client -> gateway -> mock) - latency(client -> mock directly) +``` + +measured through the same client over the same loopback. The direct-to-mock path is the zero point. + +## Why Rust + +The mock and the load driver must never be the bottleneck. A Python mock or closed-loop Python driver caps throughput well below where these gateways saturate, and it cannot measure tail latency honestly. The harness is a Rust (tokio) workspace so that + +- the mock and driver stay far above the gateways' saturation point +- latency is captured losslessly with `hdrhistogram` +- the driver is **open-loop** (constant arrival rate), which avoids coordinated omission and keeps the p99/p99.9 tail honest +- runs are deterministic: fixed seeds, fixed payloads, a discarded warmup window + +Only the harness is Rust. The gateways run as their real selves: LiteLLM (Python), Bifrost (Go), Portkey (the OSS JS gateway). + +## Metrics + +Coding agents stream, call tools, and send large contexts, so the streaming and payload metrics carry the most weight. + +Streaming, what an agent feels every turn + +- **TTFT overhead** - added time to the first chunk. The single most perceived metric +- **Inter-chunk latency (ITL) and jitter** - added gap between SSE chunks (p50/p99/max and standard deviation). This exposes gateways that buffer or re-chunk the stream; some coalesce the whole response and re-emit it, which is catastrophic for an agent +- **Chunk fidelity** - whether chunks pass through 1:1 or get coalesced (chunk-count ratio versus the mock) +- **Time-to-last-token** - added total stream duration + +Tool calls, core to coding agents + +- **Tool-call TTFT and total overhead** versus plain text; streamed `tool_calls` argument deltas are a heavier transform path than text +- **Tool-call correctness under streaming** - whether the gateway reassembles partial JSON argument deltas without corrupting or reordering them + +Payload scaling, agents send whole files + +- **Large-prompt overhead** - overhead as a function of input size (1k / 10k / 100k tokens), where JSON parse and serialize cost dominates + +Load and cost + +- **Throughput ceiling** - max sustained req/s before the latency knee or errors +- **Tail latency** - p99 / p99.9 added latency under concurrency +- **Resource footprint** - peak RSS and CPU at a fixed req/s, from which we derive cost per 1M requests + +## Fairness + +Since LiteLLM publishes this, transparency is the whole point. Every gateway config, image, and pinned version lives in `gateways/` and is meant to be challenged. Rules + +- each gateway runs in its **recommended production config**, not a strawman, and we run two configs per gateway: *bare passthrough* (no logging, DB, cache, or rate limit) to isolate pure proxy cost, and *realistic prod* (key auth plus a logging or spend callback on) to match what people actually run +- equal resources for every gateway (same container CPU and memory limits), so a result is never "who got more cores" +- load driver, gateway, and mock run on separate pinned cores so the driver never steals the gateway's CPU +- fixed seeds and payloads, a discarded warmup window, multiple runs with median-of-runs and variance reported + +Category differences we state honestly rather than hide + +- **LiteLLM SDK** is an in-process library with no HTTP hop, so it is a different product category from the network proxies. It is reported but kept off the same latency axis +- **OpenRouter** is SaaS only and cannot be pointed at the mock. Any number for it includes their infrastructure, the network round trip to their datacenter, and a real upstream, so it is not comparable to the self-hosted numbers. It is either excluded from the isolated-overhead charts or reported separately as an end-to-end measurement, always clearly labeled +- **Portkey** is benchmarked as the open-source self-hostable gateway, not the SaaS + +## Layout + +Each folder under `tests/` is one self-contained test: its own crate, its own driver invocation, its own assertion. + +``` +gatewaybench/ cargo workspace + crates/ + gwbench-core/ shared types: Scenario, tagged-union Outcome, + hdrhistogram-backed LatencySummary, ResourceSample, + JSONL BenchResult writer + mock-upstream/ axum mock: configurable TTFT, inter-token gap, size, SSE, usage + driver/ open-loop (constant-arrival-rate) load engine + tests/ each subdir = ONE test (bin + README describing it) + ttft/ added time-to-first-chunk + inter-chunk-latency/ added inter-chunk gap + jitter; buffering detection + chunk-fidelity/ 1:1 passthrough vs coalescing + tool-call-latency/ tool-call TTFT + streamed-arg reassembly correctness + large-prompt/ overhead vs input size (1k/10k/100k tokens) + throughput/ RPS ceiling + CPU/RSS cost per 1M requests + tail-latency/ p99/p99.9 added latency under concurrency + gateways/ per-gateway configs (bare + prod), pinned versions + litellm-proxy/ bifrost/ portkey/ openrouter/ + xtask/ `cargo xtask bench` sweeps the matrix -> results/*.jsonl + analyze/ chart generation for RESULTS.md +``` + +## Quickstart + +```bash +# build the workspace +cargo build --workspace --release + +# run the mock upstream (defaults to :8080) +GWBENCH_MOCK_PORT=8080 cargo run --release -p mock-upstream + +# run a single test against a target (e.g. a gateway on :4000, or the mock directly for the baseline) +cargo run --release -p ttft + +# sweep the full matrix and write results/*.jsonl +cargo xtask bench +``` + +## Generating the charts + +The hero charts are produced from the benchmark results by `analyze/make_hero_charts.py`. Point `_DATA` at real rows from `results/*.jsonl` and regenerate + +```bash +python analyze/make_hero_charts.py +``` + +## Status + +Scaffold: the workspace compiles, the domain types are real, and the mock serves a valid non-streaming response. The measurement internals (open-loop driver, streaming mock knobs, cgroup resource sampling, per-test assertions, the `xtask` matrix sweep) land in follow-ups. The charts show placeholder data until then. diff --git a/gatewaybench/analyze/.gitkeep b/gatewaybench/analyze/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gatewaybench/analyze/make_hero_charts.py b/gatewaybench/analyze/make_hero_charts.py new file mode 100644 index 00000000000..c3c0ca30bd4 --- /dev/null +++ b/gatewaybench/analyze/make_hero_charts.py @@ -0,0 +1,222 @@ +"""Generate the GatewayBench hero charts (CursorBench-styled scatter plots). + +The numbers here are ILLUSTRATIVE PLACEHOLDERS to convey the chart design; they +are not measured. Replace `_DATA` with real rows from results/*.jsonl once the +harness has run, then regenerate: + + python analyze/make_hero_charts.py + +Outputs analyze/throughput_vs_memory.png and analyze/ttft_vs_memory.png. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as plt +from matplotlib import font_manager + +_OUT_DIR = Path(__file__).resolve().parent +_INK = "#1f2933" +_MUTED = "#6b7280" +_GRID = "#e5e7eb" + + +@dataclass(frozen=True) +class Point: + label: str + memory_mb: float + throughput_rps: float + ttft_overhead_ms: float + color: str + note: str = "" + + +# ILLUSTRATIVE PLACEHOLDER DATA -- not measured. +_DATA: tuple[Point, ...] = ( + Point("LiteLLM Proxy", 450, 1800, 12.0, "#0b7285"), + Point("Bifrost (Go)", 180, 4200, 3.0, "#e8590c"), + Point("Portkey (OSS)", 260, 2600, 7.0, "#ae3ec9"), + Point( + "LiteLLM SDK", + 120, + 6000, + 1.5, + "#2f9e44", + note="in-process, no HTTP hop", + ), +) + + +def _style_axes(ax: "plt.Axes", title: str, ylabel: str, xlabel: str) -> None: + ax.set_title(title, loc="left", fontsize=15, fontweight="bold", color=_INK, pad=18) + ax.set_ylabel(ylabel, fontsize=11, color=_MUTED) + ax.set_xlabel(xlabel, fontsize=11, color=_MUTED) + for spine in ("top", "right"): + ax.spines[spine].set_visible(False) + for spine in ("left", "bottom"): + ax.spines[spine].set_color(_GRID) + ax.tick_params(colors=_MUTED, labelsize=10) + ax.grid(True, color=_GRID, linewidth=0.8, zorder=0) + ax.set_axisbelow(True) + + +def _annotate(ax: "plt.Axes", p: Point, dx: float, dy: float) -> None: + text = p.label if not p.note else f"{p.label}" + ax.annotate( + text, + xy=(p.memory_mb, _y(ax, p)), + xytext=(p.memory_mb + dx, _y(ax, p) + dy), + fontsize=11, + color=_INK, + fontweight="medium", + ) + if p.note: + ax.annotate( + p.note, + xy=(p.memory_mb, _y(ax, p)), + xytext=(p.memory_mb + dx, _y(ax, p) + dy - _note_gap(ax)), + fontsize=8.5, + color=_MUTED, + style="italic", + ) + + +_MODE = {"metric": "throughput_rps"} + + +def _y(ax: "plt.Axes", p: Point) -> float: + return getattr(p, _MODE["metric"]) + + +def _note_gap(ax: "plt.Axes") -> float: + lo, hi = ax.get_ylim() + return (hi - lo) * 0.045 + + +def _frontier(ax: "plt.Axes", pts: list[Point], color: str) -> None: + xs = [p.memory_mb for p in pts] + ys = [_y(ax, p) for p in pts] + ax.plot(xs, ys, color=color, linewidth=1.6, alpha=0.55, zorder=2) + + +def _scatter(pts: tuple[Point, ...]) -> None: + for p in pts: + ax = plt.gca() + ax.scatter([p.memory_mb], [_y(ax, p)], s=90, color=p.color, zorder=3, edgecolors="white", linewidths=1.2) + + +def make_throughput_chart() -> Path: + _MODE["metric"] = "throughput_rps" + fig, ax = plt.subplots(figsize=(9.2, 6.4), dpi=130) + fig.patch.set_facecolor("white") + ax.set_facecolor("white") + ax.set_xlim(80, 520) + ax.set_ylim(1000, 6800) + _style_axes( + ax, + "GatewayBench: throughput vs memory", + "sustained throughput (req/s) higher is better ->", + "peak resident memory (MB) <- lower is better", + ) + _scatter(_DATA) + frontier_pts = sorted(_DATA, key=lambda p: p.memory_mb) + _frontier(ax, frontier_pts, "#0b7285") + offsets = { + "LiteLLM Proxy": (10, 120), + "Bifrost (Go)": (10, 180), + "Portkey (OSS)": (14, 150), + "LiteLLM SDK": (-40, -320), + } + for p in _DATA: + dx, dy = offsets[p.label] + _annotate(ax, p, dx, dy) + ax.annotate( + "efficiency frontier", + xy=(120, 6000), + xytext=(300, 6300), + fontsize=12, + color=_MUTED, + fontweight="bold", + ) + ax.annotate( + "ILLUSTRATIVE PLACEHOLDER DATA -- not measured", + xy=(0, 0), + xytext=(0.99, -0.13), + xycoords="axes fraction", + ha="right", + fontsize=8.5, + color="#b91c1c", + style="italic", + ) + out = _OUT_DIR / "throughput_vs_memory.png" + fig.tight_layout() + fig.savefig(out, bbox_inches="tight", facecolor="white") + plt.close(fig) + return out + + +def make_ttft_chart() -> Path: + _MODE["metric"] = "ttft_overhead_ms" + fig, ax = plt.subplots(figsize=(9.2, 6.4), dpi=130) + fig.patch.set_facecolor("white") + ax.set_facecolor("white") + ax.set_xlim(80, 520) + ax.set_ylim(0, 14) + _style_axes( + ax, + "GatewayBench: streaming TTFT overhead vs memory", + "p99 added time-to-first-chunk (ms) <- lower is better", + "peak resident memory (MB) <- lower is better", + ) + _scatter(_DATA) + frontier_pts = sorted(_DATA, key=lambda p: p.memory_mb) + _frontier(ax, frontier_pts, "#0b7285") + offsets = { + "LiteLLM Proxy": (10, 0.4), + "Bifrost (Go)": (10, 0.4), + "Portkey (OSS)": (10, 0.4), + "LiteLLM SDK": (-30, 0.6), + } + for p in _DATA: + dx, dy = offsets[p.label] + _annotate(ax, p, dx, dy) + ax.annotate( + "ideal: fast + lean (bottom-left)", + xy=(120, 1.5), + xytext=(150, 11.5), + fontsize=11, + color=_MUTED, + fontweight="bold", + ) + ax.annotate( + "ILLUSTRATIVE PLACEHOLDER DATA -- not measured", + xy=(0, 0), + xytext=(0.99, -0.13), + xycoords="axes fraction", + ha="right", + fontsize=8.5, + color="#b91c1c", + style="italic", + ) + out = _OUT_DIR / "ttft_vs_memory.png" + fig.tight_layout() + fig.savefig(out, bbox_inches="tight", facecolor="white") + plt.close(fig) + return out + + +def main() -> None: + _ = font_manager # ensure font cache import for consistent rendering + t = make_throughput_chart() + s = make_ttft_chart() + print(f"wrote {t}") + print(f"wrote {s}") + + +if __name__ == "__main__": + main() diff --git a/gatewaybench/analyze/throughput_vs_memory.png b/gatewaybench/analyze/throughput_vs_memory.png new file mode 100644 index 00000000000..af2cf537734 Binary files /dev/null and b/gatewaybench/analyze/throughput_vs_memory.png differ diff --git a/gatewaybench/analyze/ttft_vs_memory.png b/gatewaybench/analyze/ttft_vs_memory.png new file mode 100644 index 00000000000..aca6c82f015 Binary files /dev/null and b/gatewaybench/analyze/ttft_vs_memory.png differ diff --git a/gatewaybench/crates/driver/Cargo.toml b/gatewaybench/crates/driver/Cargo.toml new file mode 100644 index 00000000000..6cf0a008036 --- /dev/null +++ b/gatewaybench/crates/driver/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "driver" +version = "0.1.0" +edition = "2021" + +[dependencies] +gwbench-core = { version = "0.1.0", path = "../gwbench-core" } +hdrhistogram = "7.6.0" +reqwest = "0.13.4" +tokio = { version = "1.53.0", features = ["rt-multi-thread", "macros"] } diff --git a/gatewaybench/crates/driver/src/lib.rs b/gatewaybench/crates/driver/src/lib.rs new file mode 100644 index 00000000000..d19de32c4ad --- /dev/null +++ b/gatewaybench/crates/driver/src/lib.rs @@ -0,0 +1,27 @@ +#![forbid(unsafe_code)] + +use std::fmt; + +use gwbench_core::{LatencySummary, Scenario}; +use hdrhistogram::Histogram; + +#[derive(Debug)] +pub struct DriverError { + message: String, +} + +impl fmt::Display for DriverError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(&self.message) + } +} + +impl std::error::Error for DriverError {} + +/// Runs a stub open-loop constant-arrival-rate engine against `target`. +pub async fn run(_scenario: &Scenario, _target: &str) -> Result { + let histogram = Histogram::::new(3).map_err(|error| DriverError { + message: error.to_string(), + })?; + Ok(LatencySummary::from_histogram(&histogram)) +} diff --git a/gatewaybench/crates/driver/src/main.rs b/gatewaybench/crates/driver/src/main.rs new file mode 100644 index 00000000000..a513727e39f --- /dev/null +++ b/gatewaybench/crates/driver/src/main.rs @@ -0,0 +1,6 @@ +#![forbid(unsafe_code)] + +#[tokio::main] +async fn main() { + println!("driver stub: open-loop load engine is not implemented"); +} diff --git a/gatewaybench/crates/gwbench-core/Cargo.toml b/gatewaybench/crates/gwbench-core/Cargo.toml new file mode 100644 index 00000000000..80bdb61c66e --- /dev/null +++ b/gatewaybench/crates/gwbench-core/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "gwbench-core" +version = "0.1.0" +edition = "2021" + +[dependencies] +hdrhistogram = "7.6.0" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.150" +thiserror = "2.0.19" diff --git a/gatewaybench/crates/gwbench-core/src/lib.rs b/gatewaybench/crates/gwbench-core/src/lib.rs new file mode 100644 index 00000000000..534116f4c24 --- /dev/null +++ b/gatewaybench/crates/gwbench-core/src/lib.rs @@ -0,0 +1,122 @@ +#![forbid(unsafe_code)] + +use std::io::Write; +use std::time::Duration; + +use hdrhistogram::Histogram; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum CoreError { + #[error("failed to serialize benchmark result: {0}")] + Serialization(#[from] serde_json::Error), + #[error("failed to write benchmark result: {0}")] + Io(#[from] std::io::Error), +} + +mod duration_millis { + use serde::{Deserialize, Deserializer, Serializer}; + use std::time::Duration; + + pub fn serialize(duration: &Duration, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u64(duration.as_millis() as u64) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let millis = u64::deserialize(deserializer)?; + Ok(Duration::from_millis(millis)) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Scenario { + pub name: String, + pub target_rps: u32, + pub concurrency: u32, + pub streaming: bool, + pub prompt_tokens: u32, + pub completion_tokens: u32, + #[serde(with = "duration_millis")] + pub duration: Duration, + #[serde(with = "duration_millis")] + pub warmup: Duration, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "kind")] +pub enum Outcome { + Success { status: u16 }, + Timeout, + TransportError { message: String }, + UpstreamError { status: u16 }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LatencySummary { + pub count: u64, + pub p50_ms: f64, + pub p90_ms: f64, + pub p99_ms: f64, + pub p999_ms: f64, + pub max_ms: f64, +} + +impl LatencySummary { + /// Converts histogram values recorded in microseconds to millisecond percentiles. + pub fn from_histogram(histogram: &Histogram) -> Self { + let micros_to_millis = |micros: u64| micros as f64 / 1_000.0; + Self { + count: histogram.len(), + p50_ms: micros_to_millis(histogram.value_at_quantile(0.50)), + p90_ms: micros_to_millis(histogram.value_at_quantile(0.90)), + p99_ms: micros_to_millis(histogram.value_at_quantile(0.99)), + p999_ms: micros_to_millis(histogram.value_at_quantile(0.999)), + max_ms: micros_to_millis(histogram.max()), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ResourceSample { + pub rss_bytes: u64, + pub cpu_millis: u64, + pub ts: u64, +} + +impl ResourceSample { + /// Stub resource sampler; real `/proc` and cgroup sampling will land later. + pub fn sample() -> Result { + let ts = match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) { + Ok(duration) => duration.as_millis() as u64, + Err(_) => 0, + }; + Ok(Self { + rss_bytes: 0, + cpu_millis: 0, + ts, + }) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BenchResult { + pub gateway: String, + pub scenario: Scenario, + pub latency: LatencySummary, + pub resources: ResourceSample, +} + +impl BenchResult { + pub fn append_json_line(&self, writer: &mut W) -> Result<(), CoreError> { + serde_json::to_writer(&mut *writer, self)?; + writer.write_all(b"\n")?; + Ok(()) + } +} diff --git a/gatewaybench/crates/mock-upstream/Cargo.toml b/gatewaybench/crates/mock-upstream/Cargo.toml new file mode 100644 index 00000000000..f5ff9fe3b6c --- /dev/null +++ b/gatewaybench/crates/mock-upstream/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "mock-upstream" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.8.9" +serde_json = "1.0.150" +tokio = { version = "1.53.0", features = ["rt-multi-thread", "macros", "net"] } diff --git a/gatewaybench/crates/mock-upstream/src/main.rs b/gatewaybench/crates/mock-upstream/src/main.rs new file mode 100644 index 00000000000..6143920c3ce --- /dev/null +++ b/gatewaybench/crates/mock-upstream/src/main.rs @@ -0,0 +1,37 @@ +#![forbid(unsafe_code)] + +use axum::{routing::post, Json, Router}; +use serde_json::{json, Value}; + +async fn chat_completions() -> Json { + Json(json!({ + "id": "chatcmpl-gwbench", + "object": "chat.completion", + "created": 0, + "model": "mock-upstream", + "choices": [{ + "index": 0, + "message": {"role": "assistant", "content": "stub response"}, + "finish_reason": "stop" + }], + "usage": { + "prompt_tokens": 1, + "completion_tokens": 2, + "total_tokens": 3 + } + })) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let port = std::env::var("GWBENCH_MOCK_PORT") + .ok() + .and_then(|value| value.parse::().ok()) + .unwrap_or(8080); + let app = Router::new().route("/v1/chat/completions", post(chat_completions)); + let listener = tokio::net::TcpListener::bind(("0.0.0.0", port)).await?; + println!("mock upstream listening on {listener:?}"); + // Streaming and configurable response timing are intentionally deferred. + axum::serve(listener, app).await?; + Ok(()) +} diff --git a/gatewaybench/gateways/bifrost/.gitkeep b/gatewaybench/gateways/bifrost/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gatewaybench/gateways/bifrost/README.md b/gatewaybench/gateways/bifrost/README.md new file mode 100644 index 00000000000..0c469516bf8 --- /dev/null +++ b/gatewaybench/gateways/bifrost/README.md @@ -0,0 +1 @@ +Placeholder for Bifrost benchmark configuration. diff --git a/gatewaybench/gateways/litellm-proxy/.gitkeep b/gatewaybench/gateways/litellm-proxy/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gatewaybench/gateways/litellm-proxy/README.md b/gatewaybench/gateways/litellm-proxy/README.md new file mode 100644 index 00000000000..6ea3db2b315 --- /dev/null +++ b/gatewaybench/gateways/litellm-proxy/README.md @@ -0,0 +1 @@ +Placeholder for LiteLLM proxy benchmark configuration. diff --git a/gatewaybench/gateways/openrouter/.gitkeep b/gatewaybench/gateways/openrouter/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gatewaybench/gateways/openrouter/README.md b/gatewaybench/gateways/openrouter/README.md new file mode 100644 index 00000000000..dc79b80a95c --- /dev/null +++ b/gatewaybench/gateways/openrouter/README.md @@ -0,0 +1 @@ +Placeholder for OpenRouter benchmark configuration. diff --git a/gatewaybench/gateways/portkey/.gitkeep b/gatewaybench/gateways/portkey/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gatewaybench/gateways/portkey/README.md b/gatewaybench/gateways/portkey/README.md new file mode 100644 index 00000000000..86bdf395a23 --- /dev/null +++ b/gatewaybench/gateways/portkey/README.md @@ -0,0 +1 @@ +Placeholder for Portkey benchmark configuration. diff --git a/gatewaybench/rust-toolchain.toml b/gatewaybench/rust-toolchain.toml new file mode 100644 index 00000000000..292fe499e3b --- /dev/null +++ b/gatewaybench/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" diff --git a/gatewaybench/tests/chunk-fidelity/Cargo.toml b/gatewaybench/tests/chunk-fidelity/Cargo.toml new file mode 100644 index 00000000000..7687a78e175 --- /dev/null +++ b/gatewaybench/tests/chunk-fidelity/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test-chunk-fidelity" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/tests/chunk-fidelity/README.md b/gatewaybench/tests/chunk-fidelity/README.md new file mode 100644 index 00000000000..8342c4b46f7 --- /dev/null +++ b/gatewaybench/tests/chunk-fidelity/README.md @@ -0,0 +1 @@ +This test measures whether a gateway passes server-sent event chunks one-to-one or coalesces them. It reports the gateway-to-upstream chunk-count ratio. diff --git a/gatewaybench/tests/chunk-fidelity/src/main.rs b/gatewaybench/tests/chunk-fidelity/src/main.rs new file mode 100644 index 00000000000..954a93ed039 --- /dev/null +++ b/gatewaybench/tests/chunk-fidelity/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("chunk-fidelity: SSE chunk-count ratio vs upstream"); +} diff --git a/gatewaybench/tests/inter-chunk-latency/Cargo.toml b/gatewaybench/tests/inter-chunk-latency/Cargo.toml new file mode 100644 index 00000000000..d81f9c05c7c --- /dev/null +++ b/gatewaybench/tests/inter-chunk-latency/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test-inter-chunk-latency" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/tests/inter-chunk-latency/README.md b/gatewaybench/tests/inter-chunk-latency/README.md new file mode 100644 index 00000000000..0d2e2158407 --- /dev/null +++ b/gatewaybench/tests/inter-chunk-latency/README.md @@ -0,0 +1 @@ +This test measures added inter-chunk gap and jitter at p50, p99, and max. It detects gateways that buffer or re-chunk server-sent events. diff --git a/gatewaybench/tests/inter-chunk-latency/src/main.rs b/gatewaybench/tests/inter-chunk-latency/src/main.rs new file mode 100644 index 00000000000..f0867fd0005 --- /dev/null +++ b/gatewaybench/tests/inter-chunk-latency/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("inter-chunk-latency: added inter-chunk gap and jitter"); +} diff --git a/gatewaybench/tests/large-prompt/Cargo.toml b/gatewaybench/tests/large-prompt/Cargo.toml new file mode 100644 index 00000000000..9641a566dcd --- /dev/null +++ b/gatewaybench/tests/large-prompt/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test-large-prompt" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/tests/large-prompt/README.md b/gatewaybench/tests/large-prompt/README.md new file mode 100644 index 00000000000..8d95f44580f --- /dev/null +++ b/gatewaybench/tests/large-prompt/README.md @@ -0,0 +1 @@ +This test measures gateway overhead as input size grows. It compares scenarios using 1k, 10k, and 100k input tokens. diff --git a/gatewaybench/tests/large-prompt/src/main.rs b/gatewaybench/tests/large-prompt/src/main.rs new file mode 100644 index 00000000000..aa7eef5bcae --- /dev/null +++ b/gatewaybench/tests/large-prompt/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("large-prompt: overhead across 1k, 10k, and 100k token inputs"); +} diff --git a/gatewaybench/tests/tail-latency/Cargo.toml b/gatewaybench/tests/tail-latency/Cargo.toml new file mode 100644 index 00000000000..663317c8761 --- /dev/null +++ b/gatewaybench/tests/tail-latency/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test-tail-latency" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/tests/tail-latency/README.md b/gatewaybench/tests/tail-latency/README.md new file mode 100644 index 00000000000..b66eb5100cd --- /dev/null +++ b/gatewaybench/tests/tail-latency/README.md @@ -0,0 +1 @@ +This test measures p99 and p99.9 added latency under concurrency. It is intended to expose gateway behavior in the latency tail. diff --git a/gatewaybench/tests/tail-latency/src/main.rs b/gatewaybench/tests/tail-latency/src/main.rs new file mode 100644 index 00000000000..6eff6c23601 --- /dev/null +++ b/gatewaybench/tests/tail-latency/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("tail-latency: p99 and p99.9 added latency under concurrency"); +} diff --git a/gatewaybench/tests/throughput/Cargo.toml b/gatewaybench/tests/throughput/Cargo.toml new file mode 100644 index 00000000000..74b031e5121 --- /dev/null +++ b/gatewaybench/tests/throughput/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test-throughput" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/tests/throughput/README.md b/gatewaybench/tests/throughput/README.md new file mode 100644 index 00000000000..e92f492765c --- /dev/null +++ b/gatewaybench/tests/throughput/README.md @@ -0,0 +1 @@ +This test measures the maximum sustained RPS before the latency knee. It also measures CPU and RSS cost per one million requests. diff --git a/gatewaybench/tests/throughput/src/main.rs b/gatewaybench/tests/throughput/src/main.rs new file mode 100644 index 00000000000..0b9c2088c9b --- /dev/null +++ b/gatewaybench/tests/throughput/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("throughput: sustained RPS latency knee and resource cost"); +} diff --git a/gatewaybench/tests/tool-call-latency/Cargo.toml b/gatewaybench/tests/tool-call-latency/Cargo.toml new file mode 100644 index 00000000000..5f87eca7fff --- /dev/null +++ b/gatewaybench/tests/tool-call-latency/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test-tool-call-latency" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/tests/tool-call-latency/README.md b/gatewaybench/tests/tool-call-latency/README.md new file mode 100644 index 00000000000..f46242d4b5c --- /dev/null +++ b/gatewaybench/tests/tool-call-latency/README.md @@ -0,0 +1 @@ +This test measures tool-call streaming TTFT and total overhead. It also checks correctness when reassembling streamed `tool_call` argument deltas. diff --git a/gatewaybench/tests/tool-call-latency/src/main.rs b/gatewaybench/tests/tool-call-latency/src/main.rs new file mode 100644 index 00000000000..da4e91ab34b --- /dev/null +++ b/gatewaybench/tests/tool-call-latency/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("tool-call-latency: tool-call streaming TTFT and total overhead"); +} diff --git a/gatewaybench/tests/ttft/Cargo.toml b/gatewaybench/tests/ttft/Cargo.toml new file mode 100644 index 00000000000..598e76a7e46 --- /dev/null +++ b/gatewaybench/tests/ttft/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "test-ttft" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/tests/ttft/README.md b/gatewaybench/tests/ttft/README.md new file mode 100644 index 00000000000..469052a72a5 --- /dev/null +++ b/gatewaybench/tests/ttft/README.md @@ -0,0 +1 @@ +This test measures added time-to-first-chunk (streaming TTFT) overhead compared with a direct-to-mock baseline. It will report gateway overhead for the first streamed response chunk. diff --git a/gatewaybench/tests/ttft/src/main.rs b/gatewaybench/tests/ttft/src/main.rs new file mode 100644 index 00000000000..d6bd0826218 --- /dev/null +++ b/gatewaybench/tests/ttft/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("ttft: added time-to-first-chunk overhead vs direct-to-mock baseline"); +} diff --git a/gatewaybench/xtask/Cargo.toml b/gatewaybench/xtask/Cargo.toml new file mode 100644 index 00000000000..337bfe3d6d6 --- /dev/null +++ b/gatewaybench/xtask/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/gatewaybench/xtask/src/main.rs b/gatewaybench/xtask/src/main.rs new file mode 100644 index 00000000000..5b4b75854d0 --- /dev/null +++ b/gatewaybench/xtask/src/main.rs @@ -0,0 +1,8 @@ +#![forbid(unsafe_code)] + +fn main() { + match std::env::args().nth(1).as_deref() { + Some("bench") => println!("would sweep the matrix"), + _ => println!("usage: cargo xtask bench"), + } +}