From 343979cd314fd560f45c82170b2bf6edf07a5500 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 23:04:07 +0000 Subject: [PATCH] feat(gatewaybench): scaffold AI-gateway overhead benchmark Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com> --- gatewaybench/.gitignore | 2 + gatewaybench/Cargo.lock | 1921 +++++++++++++++++ gatewaybench/Cargo.toml | 3 + gatewaybench/README.md | 136 ++ gatewaybench/analyze/.gitkeep | 0 gatewaybench/analyze/make_hero_charts.py | 222 ++ gatewaybench/analyze/throughput_vs_memory.png | Bin 0 -> 90326 bytes gatewaybench/analyze/ttft_vs_memory.png | Bin 0 -> 87310 bytes gatewaybench/crates/driver/Cargo.toml | 10 + gatewaybench/crates/driver/src/lib.rs | 27 + gatewaybench/crates/driver/src/main.rs | 6 + gatewaybench/crates/gwbench-core/Cargo.toml | 10 + gatewaybench/crates/gwbench-core/src/lib.rs | 122 ++ gatewaybench/crates/mock-upstream/Cargo.toml | 9 + gatewaybench/crates/mock-upstream/src/main.rs | 37 + gatewaybench/gateways/bifrost/.gitkeep | 0 gatewaybench/gateways/bifrost/README.md | 1 + gatewaybench/gateways/litellm-proxy/.gitkeep | 0 gatewaybench/gateways/litellm-proxy/README.md | 1 + gatewaybench/gateways/openrouter/.gitkeep | 0 gatewaybench/gateways/openrouter/README.md | 1 + gatewaybench/gateways/portkey/.gitkeep | 0 gatewaybench/gateways/portkey/README.md | 1 + gatewaybench/rust-toolchain.toml | 2 + gatewaybench/tests/chunk-fidelity/Cargo.toml | 6 + gatewaybench/tests/chunk-fidelity/README.md | 1 + gatewaybench/tests/chunk-fidelity/src/main.rs | 3 + .../tests/inter-chunk-latency/Cargo.toml | 6 + .../tests/inter-chunk-latency/README.md | 1 + .../tests/inter-chunk-latency/src/main.rs | 3 + gatewaybench/tests/large-prompt/Cargo.toml | 6 + gatewaybench/tests/large-prompt/README.md | 1 + gatewaybench/tests/large-prompt/src/main.rs | 3 + gatewaybench/tests/tail-latency/Cargo.toml | 6 + gatewaybench/tests/tail-latency/README.md | 1 + gatewaybench/tests/tail-latency/src/main.rs | 3 + gatewaybench/tests/throughput/Cargo.toml | 6 + gatewaybench/tests/throughput/README.md | 1 + gatewaybench/tests/throughput/src/main.rs | 3 + .../tests/tool-call-latency/Cargo.toml | 6 + .../tests/tool-call-latency/README.md | 1 + .../tests/tool-call-latency/src/main.rs | 3 + gatewaybench/tests/ttft/Cargo.toml | 6 + gatewaybench/tests/ttft/README.md | 1 + gatewaybench/tests/ttft/src/main.rs | 3 + gatewaybench/xtask/Cargo.toml | 6 + gatewaybench/xtask/src/main.rs | 8 + 47 files changed, 2595 insertions(+) create mode 100644 gatewaybench/.gitignore create mode 100644 gatewaybench/Cargo.lock create mode 100644 gatewaybench/Cargo.toml create mode 100644 gatewaybench/README.md create mode 100644 gatewaybench/analyze/.gitkeep create mode 100644 gatewaybench/analyze/make_hero_charts.py create mode 100644 gatewaybench/analyze/throughput_vs_memory.png create mode 100644 gatewaybench/analyze/ttft_vs_memory.png create mode 100644 gatewaybench/crates/driver/Cargo.toml create mode 100644 gatewaybench/crates/driver/src/lib.rs create mode 100644 gatewaybench/crates/driver/src/main.rs create mode 100644 gatewaybench/crates/gwbench-core/Cargo.toml create mode 100644 gatewaybench/crates/gwbench-core/src/lib.rs create mode 100644 gatewaybench/crates/mock-upstream/Cargo.toml create mode 100644 gatewaybench/crates/mock-upstream/src/main.rs create mode 100644 gatewaybench/gateways/bifrost/.gitkeep create mode 100644 gatewaybench/gateways/bifrost/README.md create mode 100644 gatewaybench/gateways/litellm-proxy/.gitkeep create mode 100644 gatewaybench/gateways/litellm-proxy/README.md create mode 100644 gatewaybench/gateways/openrouter/.gitkeep create mode 100644 gatewaybench/gateways/openrouter/README.md create mode 100644 gatewaybench/gateways/portkey/.gitkeep create mode 100644 gatewaybench/gateways/portkey/README.md create mode 100644 gatewaybench/rust-toolchain.toml create mode 100644 gatewaybench/tests/chunk-fidelity/Cargo.toml create mode 100644 gatewaybench/tests/chunk-fidelity/README.md create mode 100644 gatewaybench/tests/chunk-fidelity/src/main.rs create mode 100644 gatewaybench/tests/inter-chunk-latency/Cargo.toml create mode 100644 gatewaybench/tests/inter-chunk-latency/README.md create mode 100644 gatewaybench/tests/inter-chunk-latency/src/main.rs create mode 100644 gatewaybench/tests/large-prompt/Cargo.toml create mode 100644 gatewaybench/tests/large-prompt/README.md create mode 100644 gatewaybench/tests/large-prompt/src/main.rs create mode 100644 gatewaybench/tests/tail-latency/Cargo.toml create mode 100644 gatewaybench/tests/tail-latency/README.md create mode 100644 gatewaybench/tests/tail-latency/src/main.rs create mode 100644 gatewaybench/tests/throughput/Cargo.toml create mode 100644 gatewaybench/tests/throughput/README.md create mode 100644 gatewaybench/tests/throughput/src/main.rs create mode 100644 gatewaybench/tests/tool-call-latency/Cargo.toml create mode 100644 gatewaybench/tests/tool-call-latency/README.md create mode 100644 gatewaybench/tests/tool-call-latency/src/main.rs create mode 100644 gatewaybench/tests/ttft/Cargo.toml create mode 100644 gatewaybench/tests/ttft/README.md create mode 100644 gatewaybench/tests/ttft/src/main.rs create mode 100644 gatewaybench/xtask/Cargo.toml create mode 100644 gatewaybench/xtask/src/main.rs 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 0000000000000000000000000000000000000000..af2cf5377346eec4e52f316db1a9873b4bcd776a GIT binary patch literal 90326 zcmeFZbySt@+by~fkdy{N1f;u>4(U|78>FSXm2Om8TBKX)?(XjH?v8Ww`u@Ie??28M zdyI3=-v69u42KG9J?ma~T-TiQn%5I7_d)yx;wwZ51oA>sLPP-qfr*AdpxNM`gP-i9 zyO4vwxE(~*92BjM9h~*;j3Dpz9c(^XJA5)Tc;jSbXK!Y0#md0U#K1)N#?-;V#-5vz z(el4u!C-A?!WeC*W(V$qU?ZV!4}oAlJpF-!i{|o!KtUjqB7#aTNqh5-F8Hbw&yQ4D zScUtT6=ftp1tui)QnOl{RhI4JG|Xd-viq04pJlUbHH*jF$9)#S8DVH_{Qc~HS?ySj zh3YrytgH9nXm$#ZvvGt2Jr{S)Wtap-RQEfPf3LCk2Jhki>wou1m{R}w%`Xg!=%9Zu z333@K^nb5!Qt@&m|G8qA(*N^%5gL7=;Qzcqsl^ZDKX1tOLm>ap>lG0GS3aQZEdj*w zz|MdLg@EtNY3XrQ$~XUz9;va3iRO2@M8?KhHjYj{k(HDIfv|>sITG=>_!itBcZeUa zCL2p#X9rL&cTFu?n}#6l6DmizdjZU(q)g5e4)F;;<~pd}9-bCAcE;taAk947t@DJz z6J@0bjcsl)hVdD=;7&Z=_y+vMi~ntB$86lAB{=)@`p@yU_~V4k-#NNR#+33LYwr!d z+KOVdWv3C_e%Sf(N@;TJ4J8L^_MoU?|BOI-c6Q+9f(N!UeafPRExqOg=YVJ@;>-x< z;N{GDj^X?Me$%`HAtNK~qRdRg$Mg)f<8_*Xt0irjL|&cI<>435%*&$pNQ;$ElbOR| z)2&HVX1uSf<}lbwKoP|~GBosgFKJFEB;>}or-z33_5{Dm?yd4@I@&+;Jq+oh zHa0dAii2nDpQcfAm1qt}$L7Lxy>ufZ7u^*V_5ZHsFr?E@fyi#%oMz#A$s2KhA?4s| zXEbtYbv^ftGeIUGz6we0&jn*pyGA_$h1u82Y`BeqB#K3y-+CxAVd14Waf`6`4CG|< z2Sa~1$`(osxZm%UQ0q6}Bw#EfP<%_4E615QIChUXze)J%>l5SR;P`ZOQ25^)t*>+1 z-C;3MQap{{U$i}^pg>4el)S90T+iI#*=2JPJkhHV(Np8r@peg@y5B41mgS6Z_O_Vd zvr!J@wp8WR{2JLUTc@{hf9cdlg!IsC?QAtoOuSka7B2nR{2KYi3l+|yyqZ_Uq!b7# ziQ=(~zLmP(>O+Uen>x)8!s6?*gM8QU565?kKKz$_Cu|>5g1=ky@NliPJv6S*B1o#* z9C*-y!L6E`zv}6sl~Y&t|M5cu_tjQw&j>~Xe@Z-?7oD+e>N99)&+AfE#Ze}s1BK@E z==SNI&2sOR`$z%o3eQ_uiU|C|GH-D)5oARPs_{t<2e-b$2+@4`s7zE) z)$V(_U-Nk(R$s#ojgNmxt$cZ)T>*b;EPHrTs`WvpV}hU-9n%?onyVU2kxxnk;im$+ zZKJ&rV&55tbK83^6Hbw z>~v5PplnP-Tf9HRk4WVWuk#Ta8y~0jOrlmT)^Cm74~hsER&ip;%gakIE{4OmpoKw2 zXbQ`XVsdR!3u^CCm2*C;9hv8hp=K;@suZ{nfewr$rsNd`mXF^nB zQ6YVM{ZuLF1B50->%JSXlQVHno{n4vV=e|f?;Ye2-+H;#lJd`!@~Nv=UAJ54lZS; zqwmwRuzb}RCMhCW&?-Yu~>g!=f=sCB9fzFGL$(N*gu zsexL2-C;t`>DBh60Gc;5RE)B6N#&pBw$hB_vsKjQiH@343sog012Bo4t`vx>MOi-! z>Yf>t!h;|OkA%`vH$BR%zn6m&dAL>nEjcRC+WQVxTsBEe-WCC@KeN*rJn#9IT@`2L z9YI|8$EhkuxC!fpfaozci67)P_KqEO>Z8X?_s)W{s+FTt zEnzIBs3<~cHo<=L_yqruW`7Zcl$)F9SUA|LpOlarwC|C0T$jywkGr7fQ5%pHaBL7h!)ZZ014@k?0C43a>k3IXkFdN8KkGg~F)$+Wzh=i3SDQ|?Rf z4L1kk1vht%@Mo8>5Es`PL#2v-C=7qw5l)zXHj^x0 zF<0uTP!dyPIGmvEj=D0=u_$b*om^L#o(?6?Y#S63EPR&AD)OX$T#$LcROlX5zoajFzum9&b; zxd%q1G@TaL1^<}Q5G=?Z5L+Ibrs_+6`y$ozjW(eeDG!jk8b z>OIx?*w{+bvY}4}f}wa%lq7bHvddBMCc5+T_m-6a!-IOO*obEB@I; zt!rSJikB|&C^ zk(Cy%u|PAD4-Um}b!Pko=7YPp)m#Yb!)^7FbJ{)^%OATE%T2qT&~ww_Nyv;3(oM39*r3MI)pAO{;IYsW9rSN!QIpnG9Q}L4JnUrBNwUe zZ^sggA0rmL`}$;d<1)U~~B1pzwZ3ly!s z4Z_SGMBG_|d@lWlo+X=Lqq%BoZq?D5TGVzie_L}f_x{o2QrM&BgvTEyfQ9Gsb(60w zZ)6sQ{^CV&s^2f#oZRYb)a~do3o6=Tw6kg(YN!|C;V1i{2*Ez6@80$9rD(<^#P|lL zctC-vJ*il~L~O*Pr>E^~5EjRZ=^qJ$dN)ix-@p2fW}6Q2Kik z%x@P{b_gZ>{As{@+0zKq{p(_);2w;ZdVrIfhN1bVJE6)CnHuHd#&aV7j|!TTtSraN zA&hQ&ZuRsa6*E4b&VMjT@x1Gf0~>s6;+oJs>-3|7p&=%r+YW3d5k-t-&37NsOWv@t zvNNmmM&)+$OhoV*5y`yavJGmE(whPJPHaLA=x@E z@6AZk$3g5p8m81;P$>BE(cn|TS0x^&6*d|g8tj_`0SBdJPv_~M*C(l}9>?J$A4m3& zCT<@oB`C_tW2BBBI#X8`iHe#y1Pai}qGo8Q=vwd(J#ritRTuAryc=%yvm2ocKct3s ze7_|UHBxr0H1PfzwO9z?X5HD%tjltFCDwOIOVQ-Wc4vY6yrG5J;>Upn@7Tp!nUZ-B z&fd>rnGB3`l#xBm{NOgz3S^Z9ur0^=thiVduB2=?ga`&2+9QSncR;K&tt6+a)jm87 zx2S}yt8WaeHSl?BXD{Z4vGIwzEC}~iU{z}`2(dhZZtn6A*UQ}s=;%sLqGG+7inY#3 z6-JAY2IgyuW73=Z@#wt|=V-4g&>?;OeI)?BxVR#V89BavSg2N&JEf$dK+*NQ#MJRR z*r*&LB@I|jB%0`|B*#E7SYb5S%jt?Woaq;PreXX{t1ABUk$yTPjcLE(yv{OMgrusr zTJLH|juZKJV08ku%zGKR#i7qWEhMkG-GtCAKrZomv?on*UD1a$tsxjLuWh}lnqAPv zuhbt1%k|man{k#~hVEIp)@JOywi`G1H^R3xv#?lxI?vI;t5_H3V`@~RQ)9o!Sjec* zLO1$-WGGruExc5#c9&A|EA>9sk}c$0Hw1{pf7R1M_2 zMb#gCX!Y?N6ga2i<$I1aQ$WX^k|5`}D!ef@Ov+@xL#CvxY&gF2)`!g8{tpa(x#}p*>@1Rexqnb)G3?R&yRIXP0xSgDC7m|aR;49?uONk$>#1Ac zPOj^zzNAYLz`wYt_{t_Nr=;Zr@_$>K?%r{2JUcsk%XBw-@1goL@C^&@?MW3BMqwhi z;~aOPAc~aXonXCf`S>vNz12fEHWIxVWx<&_Ka)Xrvj0|@92wr3P1}g6H8|7+LE7vW z`kadT>eVZor6b$su!*M(eko3SG~L;<{)3>Mke>|;X=6adRTLJ4ZqWyRCvaqD4_rpo@(p2zj=bMMV^be2U zHrJWejv^O#@e;ba`U3JpF#!u$>&|rC<)HOoGSeihsgGH_r<$r@;%L2i(bzoQEx&Lv z%rd=ZiJOlu%g=HP({+Ur29&Vx9I@=2kDKTB3 zqw`*IZQjiSAO#=;7)J%Sku?PR-XcIxY009WxJWg{=$!g!iCud?**i5;G@SXOt_G8aD2^Z7xa z`x%*vS3n+k+OQ;s*al=Xsf$`+9pu9k&hJ=G)&a!ot2sn~aOysnVE+hIQu; zkJM-Gm%7DE4YeACqa&iHUxC2U8{BF1gPS&xRd!spPA0^uZjVM47f*Ht4v#U%jH}*9jv= zUtPw-g5r48w>c%sI(kn7(W*@;ns8)g`D0r+kL6;`hzh<**naJ0_v38&;hUt_d$9L; z7UniZ?-hh~Wl@ENePBF)&Wy>MYYLbx*QuYR4$hm#Nst8v*S_KO5~-b*eAoLa@F|?_ z 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%0A36@ATbA;Y@uf0IOGk&v?z!oI)j}fI%m4n z;9g2&n5mAOK8b$APfcbDsuV3u!cK&`mZ#LNzk_!N8EM3Ph8#CxYVdS%sT`!!z&!|? z>AT%VPfrW9cw0e2&lg7frbbK0H(ZwX4-n}e? z>0|3c%M}rN##?itu7jXL?gMQryL3iji`a#e$dSGCK$3PwC_a$ zu5Yc3!AE+=g%0#$A?mz$!gOf!6p}z91I{oX9^5{31PH7BOL%v2;+6F?Y3}o1P3A`~ z(-@}*Ycy5$CfhUb!eD9)tfyGI-U;I%I6YxxpnCn)UpyB*PEBclF1Yuzt_YG)dDEq;E*wJD_T79tu}2 zSTrBa3!b(LmZA;nezCoPMdAcpH7?D|wN<*2ooc6t_+r{Pesz%rK_=Q{Y?4kA4 zx76=Yx9iRZynMO46&3MKjS_a^VCJW}1I>JJh}kK4!v9LMZ&A#2C}fp=auCKCCc6cd zpDelsRe~WGC|iS`UR5(a;#E5;r@=Fpy7Lzl9gozZXvap(iCLk*C&Z&Sr(FX<(toP zBwUp6Q-V@OxSSMn>mTflcXgSL6+4THoc^*e5Q911g~i?xIxam*m=XnsXSL@U5TVJ7 zUnI4ocSyg#0o6q#*)4tbcNnPnhMS)EsGd}l_8*$rHB)|^U+ z9;?1cEs%6IHSI@33{fqr|1B=OVN+1|x41BYAU6gi&6&4zxM&%$RI*nx^@iF=F;Q)P zIK)=xovIQ63m_|!^K*oA-6`atLNJD{-=k}^HSI& z|C#~&hiWf>tA7-xSP1}ip8?IHh9QcvjjX$*%Gq~P<*glp6<`b;xB9lJ-B>cGCM&V(~uMqyh;C)KvB#>OFQ8TOKl5fjA$aPnkXuJM5 zIpX>GYYK2-KtpV`wj*vZun^8#pLU(u+K1uh%y{7erA5lf5$8KMnMzE|PIK^j5o5Og0q+9W<*Hhv_Yp;C3SF;h(g`b-PX z^{31;9xL7m8@Ut`gpI?HW$Cz3&y`mgb3x|u3qZKNKhr3o!Kwzs39X({LcXokHF$d~ zU0-h()CZnQqJy0dgbo21-17s@qfCldWNC_L;Gz*D0s%^Li!k!g82TbD8tTfYHqj*f}5PxxT1g9V7;cwhAiM!Zj{Vcwd{do6-9@mbM&HAl6F-UNsDA!DxQ z#od|n?FR{o_x5WJCu7(`0enRz!aCHELnyh`P=bC@*J|_@uQ>km()}%6YEZ+ruA4*w zq3rO;SaBG1M5GR9)f}4(zPH@i7zEVNth92nmmyXb|N8ZixZ)<7g}Ht`K^(;(?EFh$p|B%<)#HIS{0UQ&(ELDcbaJ~P)i*h zWgp0WE6FC~Bn=;nE~O}+y&Ju&7qm;Q8eoVU!2@aZ3N>wk+)H7=1;x%t7<5rYnq z+O6L>aGOML@e`ZIUOP#}?y>ol=gi5@W7zv$NnBL7XT9(c?7#O0tZZx7l&v{klMO5! zHYKZLx%HE;Pm`=z5Za_HUSwI_9%NT z7NU^OoDY;U(G87g8n(Uncwi^L;HR#+psp$V`SA3|xagvD{jqDS2CvX=@oWRoCNIv9 zSm{;B-a1Na6&rHFXdJc3v%_2YPwg^{lcleL2DS7 zo9!-)cT!*gXz ze8~Bkg7c`0U#4JeHW%xCj;^l5)jO@DKI^mOEaFpNdt}Y}ln0MH@%A;$4UN0$*jP0t z4?m}h9o{2Z&sl3D{f>g7@x_GtoSG^y;$2;{|JQaRu zcBSWS5Z}yi%=)wDF!1IAauU*TAW8&QnbXX0^yJ3=S9OYVeGp!T#2;yiGrc(wdFvT( z^`Ti-&r)D=XT_9fZ!vTH@C*C;d$@>ypS@N0^S_IUdvMxpUZjEC@Tx3pyP7)~evLY! z+LV8(aR0+h3I=TJdhSKWcH&cby?Dd`4QaRZD^M=8Xd*FI=<+uWv|Jed2+zy9Q$|}4 zQad*YFFChe&r`lB{ii*(kzC_x!sI^&>g-OR~F2voAqv7wq-A zBP>3x>aQ@>RDVwJz^VcVIl$95GZP8RL&FeG6FhtVlacz6Q{ROLIpzbhJ}tY~7`gOL z6NU4U19Be;ufQNw?xXV!Xp!84bQvu2zHQZ3B7B%lrBN5k!7S~ls?t?#ofSusx{+~4r8FN^mR*>gF~(i8s^CttzhuL_y+M@Jx%6{ARWU8mQNfcKB^fdg>bSH-*XcaZrmSP+2* zRLWvhy(c-1=g)iJzD9m6!QOtNHeF$ClPb(Ozm&s%f2bg{@>NLMj z&F%|4*b?S7oz_ne<@_~RrIl@8%D6*`rE^*@I=2-}-3n;6k60ioaC3XNVjmk<+qc-k zkws)alHW}8-d0#39=MtEPlP@?bTCc_ykQN&o0ty+<9u+}B5k_+U3s@8W z1@+TG5%0aiz2(+i`oH0Rn#&VC z%{oK^y+(0QGR>1>cA+}$Bd-$WDzhxL7Ika zJ8$e`Mt%8W|GY?Jn!+DRjR0Pi|GyASlhGF)&}D&6=|Ay<8S5&uzgwlPi*^WnN+y|b zwatl`Y&z(b)~L(&Ev4<@nAIQcMgjbLcwC;qjvNt%q3ElkXT!y>;7qujlLd0HdYm`1l0rGGteY4Kmh%@$a^wmAfnKfUOG1o3vK#zv~;C@@vzZNBRT- z!hqDBfvT?R1okHEU}i{oXrKlRczIu59fezk45WSG)!zU(%s-eaL;qr` zpbS?55`%3yq{xz2af zBM#YcdifXjDZw3D_n->SfBAB#H|whfb9lpxV|8e*VATV5!!-5RdYSr-j}5GY(!Va7dWr^n_yU^m)PKAn{2 zwEnR_%Xahi?V^KX4&^7>{)p`Be6u!G$FG6t_9SfCiHtFEaze@LGZtgVBgkM}!b5fy z``CxI%h{UYSM~SvfA|w*Tc=7yf*n&R{Br9@p0eOg(BS8KhrJRc-jS$cqu9SOh=#1h zpR~UJLsfFb;7RP#Ss|ZfW6HLalCw;&X5$nKO^75?NX|+0BeDxpI1|epT?DA*%ry!^ z`HCp7rx{2gHC(-LBg$y)NL68M$;3t=p7Y8{iL0WE=c&xYKp_GFr2M^#%{rN#Fqll- z_B^@*7G%#z^o|&KTY1GB=(x5BvFh;mp6xDM3My-B zjvCD}n=)58{{FHzl9I$j(2ll&@?pK866;-=RqN=iNAUg8X+M~802xhiLWMcFKgo#q z{u6|jwV(H?aeYvBDN(`MdI2V${bpy0!>+`XS7*eZNv*ZgCr9mzQ7#eoqn3OIo5s2@ zi9tQe`ZS>L)tpEz2MqrJOjbR&3&W5aC%>g(3~-W3VPg6C45y*$GBYPe;e<9`*B!w>`l|C zUj(|z9aKy>Q7@ZDQ_k_MYfn&MI0$Eb#2cw8qBpOX!0AQ#-eu!55;)f8gQ;Y@pH*I} zRxS>2WKtbIe!MOtFW;-V7>JtT`(vqIG|H4L8}KPNkDVg(=8dBYF66YiO>5}8sWTW? z>tteL`PPg#6>;y*9pU`lYTffom+kg;JJzlBj!Uo=QhxllD*@SOE)fyjTN$mkC*6Dp zzlE~K3 z(P4q~lWx0V0kj|C3vSOPBUmgFYm1SN#@~-u$=ZRp7Op*VmrlMtzna zL!9?0xD|Xnl*hV>BC&e^ZEC8%^Aihm$25&W>!kp*W+Z&f@pQU(E>Kq^IXx zt3BkFV;HO#Ww38IWH8ENsH2lafz&zW#NN_OBWm+!8MgzU?Qh6=-A@p@x}gHer-y8lRS*evLfOw(aA9 ziE{ryWN4g|USg~Uwyrv3`YY4$7`j}bQ>-55NRGTLs5?P%+}}UiI!T$|Uia6`uRnNA zdGY~6{#hQeOqB7>+nS(nKNseTBoM;uIHLG<2*K)>pO`a=UZQl)egE96!<*cy<&LRU z+CfzZidiR)@=E5VS!SDJPt1c06=;bYTgR`QTlMTX_xE;xUthSi?q%IjHARnIyGm=i zz1m=IU3XPeQVb}JVLgyLZfmvO*&JDXfwX2D_SIP5;)d;V|!IS-)8p%F| zmo(W+4bhtJ-eIz9{%0tNXL-00$;OhO-a3()2@0gCnunJSn>b25Vni#F4dCjDG+r^X zvkyuMyJL~d{;18+zyOEFI;zvog8a-6^;DTb*!F3p=Zx=b#mqZ`ypL$38+xs=CH&Dq zt&PRQv!?g(?*`5GKfjj6Eyii_>)jk~yBgt|A13fq2~=4m_u?M`F5& zV_e*BZMz|-Hl=BXYYd2O`@JtY>w!jFxK5Zh_P!}$G5Eaq#wV~(k~-c_ z+MJNAM~^dBp!&R5yj$1%X5mDE`8As7?h@4~SuIhN$;V0)Ulr$a%9aeG!lE*1RYTIR z3t<->0)Ous&uj4hHif~#{=$T(-w<%;sD!ap53eQ~3d_fAYF4}VO-%a_-ox-MKHx+l+Kx3^#j3r4O)i3j3S6) zSWJ2Mve`HkUPh>@4eNIY>!ugyPynfr(np560*#j>YyHMwCw;%oODWoliOI&{8C=^s z2#LnI)s?{G`(i0!)9onjHbl-0BrDL>d$z@(~|3l`0 zd`QZVL890#{NYsk$0-CtpgAcDV4O%3zF zhRADcl`GlZl;1Gaell{^PHSiR|=)P{!1(z35aH%_`-W(NjvNsKf1 zgh8Y7uM2!#Szd<|T)bInDdSL??3~JDer)K_W_UCyI{S6@e4oMa3pm{I@R(90L2_ho|ghr=G}hBW^nk`WM`F+9Y< zldwzW;LiCGRQnLVnB9@48*x4St3?Zs#$2ahzJFBlVjnun5x?Lg5IDN|XwP(J1ym7F5W6_k(0hUvB17<_yrA0Lu~v|gq- zZM0Uy?LTBo2r9>$|QP285gInxBs@cZ)zau78qmhHjSHD zm^HrJ_6+?o;gw2@Kw)Q7J1&clPVH4@S1>yc+6 zk=QK}qioWuQkcA!)Be^YXLjNR@qbbk2r}gsQoodrTZ^NbItQr`I|HBZDlxr5i#wao zWGj3vyEE2&dv5s$Dngcw1^`JPZ3yhj8IT|IAHE&1&oz6~gW~bf2FcW*@_&w$JQwrnHjhx3`hL-?()lkD#|N& zu|Hy8@VF>aMs#F%_1iJknY1JoWZsP(@~A~FM`w}JPf~|wZlWP7ss!HU-ub0rWD<>m;S1g2uN5`rca4r?|=PxWf7l%+M`G`XqMht zodiU&h^Ov-w;s~}YwN;ouPXBTvvvPo>e=nm$om4xiq4gf^qF!-J%Tk0ysZ6dnOGuCwQ01K0z9IxxzX=u zsALy8u6QBCVtV?u{QMr-2UJn$_L-8jMHajc{|E}vW>S~_vJr!L|7()#^YTA(HOQNy z{>;++S?H$$|H0Q^A-_VL@mfqeBAz^P1T9!``v(+4Q`)<|^&-5_f5X-|{bIz|!SeYg zDqw&CMh}*6ayE>9w1DV9!M7d2Q-FyTNLW`llOXC$uJU?;V+K9?%7Y(Aoz~vpimqjL z56&_)NjqF$fHrznMSEfU{Vm5D*GU~@yx8!{uQU@`?oKo-QOFtA z+xAW8cc4cD8nKO?gH*>6KkHNe*6%ZEVVNKit68=!W8}y-A!yiV!n0#ze}8I>sq5Vx zWY}X^8jqC0eefgR(&d*D%M%yVVRFFq^_ENIAd7F2UWdOeYy1(v>GJ*A@2^*1#7~ZC zEJnnq+>tO*R?wtrF5>?#=V}~l5)NX_n~BQ0 z?vNRYCto`HjcHU}wdArl{piN}MiXeb!`b6^E%Dyb_b{5&3b~ie+3h5IU$XEX{1}^y z)GWCBl$en6`In7B4ll8TL=Ocys;I(OH{Qgl>kp0yBah0>axMG1OvPrKUm6StG>S6% zTG$Mq=p|^QQwy`O-<55Mzlgj3=yZ*GOhXe5LH_R9&aq&M7%j2EOj34Xd2#c6I^*ul zMO_p0w1dW0ZS?BRN-?&*c5boctp5#HAPdh48t?nTxetzTQ^R^+3-D^xS=^+gq6&{g zzkHmy;ePflZ_Is;|23fjm-E5(p| zr46u$@?*^?NtrvZq8-V5h0YO4$y6wXR8dlKLj@?4J8k8r>(mZTj_8$*!ku$`*0QaOK_I(y38C=tfp2XsmD9FhsHy2SK+he#7#~TJr(6$4 zS62wGtxYbpfXZ!WX2I7|5O1!nNTKWPdo0A*(K&RCF48I0zGGVJwEu>DQpwc>l)>m>9A=gFVD9TOjX@~T(D zqB%+Or5l-FP#O)uUOUnf? zS5@^Ae~hNfJi8yp-6?^s2bqK7GwD8RITVFmFJtZ@eD2*D2#=%iQWQ+2PSb9Hh#q71jOlXd+mlVK4JvT6j_=R0I-`U+fjf3c8 zi${AdNNssMYVB>&Oc$e;)514<;a9{JO`5_#VkkVh+s3PUb^tq%JA3#oiuaXX-rQDd zdBfyYV?nst7Pi0oJ(5wqr+NhsUrfRmGrBb>9hHInwmL;W)l@& zo{Dm9HB~wuek2+hJ6wv1wc}q}x~ofavk?XVr4QD~w$W&PbPxogYcz7Q1LTO&6|C*@s1v3S*m7`xC3kgPpCdbU&M_es=qI`g;&UK zbH@2Pc(#Lr5k+^tu^{z$(C68@u}GOuT^7s(k6uXT3^%s7qrf9laokba#Bn$lv`@+u zeIG(90->&M9OD>7{lIebn0s@xnd3n{8+(r;benM?!ze5&5_fz^1^Gb`-|WeCycu&L z>WW%P3|$)Q;uBkYM`?5S)(ip5A2K<^`jE>5!8l=zVuO65aPr3HfYK*T_F(x8-X9Sr z&n=D6Wn?aw6nSynws*uv;2t<7#Krj)_TAtgn@mqDy5fF7)9fvJj!`~cfo+<-82DwA zikttQK*L1Guarx0{xUJ=1U$?0jU{3nt5?OP5q!DrJwbcAu{IwBgA2sb^EVN2xm9UD zNS;$ruf2V%xx0Q)TVZ#u;Nn)1R+-WL5gYE--K1l5_^vOIZ_!7E`IpCEcDs%RapLQ_ z^E(+^08Qm=SIt&J%tLxnlChrVn2u3y?5}6)4togy7KX> zhJu3J*W1<}KBb~o-)2)>r8F{CKVzJBLdqHn3wE}%rv{OJ{zr@b{GD%C8^(E!Z*f`1 z2hAohxp!h3UCz=qzkNF`d2mTDKN1Dhyi2@p8N7;n8}41;Mk8u974-chzuvh(tl1o| z#Zhpc-79W~`G^N0fxV<0un$z9jMA^zw12e_C(2>=tdQ(I^%j^|cla2= zW#-QhaWpsyy%zqR=^o+rUWn@oikArTeBQWcb3JUi+mK?bvy$e5% z;)i4&TpdfSSK>o!va}7vKGc;DXIP};d&C@t@EK(;LmGf$=ZYua!4kMMGJ;mF_hj4o z?)vWmJ7;@w3kT%+Q)Jjr3GKAIV}+)SWt##B2zDPSK6_nm;uYeigj~f-kIPD@?h;{p z1KtH(_MpwW^9}g_XwALHdbgtI!c|2tb50ggquu%vk`q?K`8NKCV^3$XRS8ouM8m=L^UQyCYCt73f`fFg%mL?bkaS zqN9UyE`o`@)8Z@1h=5~}_iWvn)1tZFlBJY6&^m0MI6OdoF78C8Ku0&F_H~9N# zeCkjtD+SU^YB~%>jcPlwjZ6(8t3W~U3#KHW@#6LkI_=OF+Dqt1X`esgvXoAk zXn8}0>L74~zipdV2Pg54Diz+hzMvDuIV!@9s2V^0H|nYs-SZz*vX!4aHZBQgr-_wk zt3eOnbosTWS?{k$ac=oIduVlI<6GjCM6oruBCM#JG%qp>e4&us<|Ndlp;EuMtUzTW z3X2*epCN6odAZ3=SLthg*#4O0OV)*d)ixPLPzphjLo2|I&Caga^z%yl6-ZU@!>OZ} z2uDJlf2*Fgwedj&gM%ycb@{Y+FLXh6zfI^?U_lf~O|4MC(zVyu5D0f%%DXJqN3JRz z6=5!AL!)trB20l-2Wk}|BYPNgT08Clht-tc>{UYpUOmbNBM ze3gXAPzjWj;SN72Hoo8j4f~Jbk1AY9q!@x8w~|K?+ufbh7JmY-YT2xfYFXBnA$A0*fFYaQFj8r^R$3_&?l@^w{w!Kt* z38&73KS8OrVoY|*z7-!0UcX0Ca+1uKLB7Sy|Pq{4)7Zw-SN~y~>=Pkna&HcS&9{wQ2 zx4fJYKB}_z<0wFOZW4ysbndHDZ1UY?C%D!g+@utOc$*JF9<{3SN|Y5!vr%%qSBc@e+Yx)wV+iU}b0KvaViF7th%E_`*(q zjEVJj14vPu4J{fyo47>zu(#gQ?Rrd3rAw+4q5(KWC;AJdA>P9QVi{*$D zfy{~_{o>?Gya=gPtvj*3BOqWH5{cyxwXZ76U6vfPXvlw^%6KdCduNMa&-U7^LmBcN zo?OVT?mV?}xtb84S>5jji`4S^cr)%0f`QVc)0T{>>)j7@lJi^3Sb-8(Om%Kzg?1oY zQ%~=x1b(q-_L0pTTw_0!>su;!>ox|FUA}5|ZrV#x!X-^6PvxH80v?tBmr#HSXafQ- z>5ad>jep%_;z>bXEgA*ywjWP9K2`={od{Hc#&7$I1KA_yM{H$e7`-M+(1?hJO}1Z& zXR9<1w_H(<*3;m07J!zfLYaE@&{&nd=)j#X{r&w{ARqXc5!vcjIoNC3B=Kb`e)c@v zfKd^rqRv||A6(jlx~r8`wJ=~$LD2;43W}osdFWPkx&!;w%5drXhw2Bo930NbJ9;Pc z;TMw%qU4nMks|rLZ&gr(JFZTG6HPuYMOc?pbp*K@J%zDuUW`M^Q>9 z2x}erR`*+$q?cOs2`H&3@hQJY0W3;DLSb6sr6qwa;dff(wdf>tb_BWO{T25_sb>8O z-C57G?H8j3@QJ||b*?B_O>5#B9WD8-KkJ|^ZI$11wEh^cd1mGST2c|H?s(Mx3L}94 zH@(y5AP!3#HnZgszNX%jV)Ui1q6F1(KSH9?7g^3-zU+?hk$M!~kcnRt-+G8Ajqy~cnnH)Xg(Xos6 z)?li#SkCZ5Uo$K)pZb(jmi9=~@clEN+ z2-Jx4n@f5ntbmIl->z(H%9@br)>j$07~IRa8M;5kZ!HC9N1eUPNlYiOt;36=bpmUeOK$RHq&_ z$nXwO9a(H_r@PILECyURvKTW~C!wU=ARr_5yT4~l+c0aau`)3}9&zFIldS_Ji#*09 zy54iS_WjiLI=K^>%pd42bi$dsp*$sMnF;d(#C8i?x3j#QZKL939SVZdU z>jhzle4$mEOjjlf6_jnoUzA;E%LC&Ki0PLu|28;JGHqHJf__1L{V!sMM+!}VfEbh@ zE$yK@an&{O79c#3i8MC3-odDioT$REn03TQzi+g<@xw1Boo+#&uFz$kGUldY&^-0w zj4d6n)p+2bF;?F+TX1~*<@^L;^v5bO4@GDYV_!UJz+IOy*L}WHv~jT6vcdj``|Q_N z>4kVUcO@NV^dc$0qcK@6@6A}?3CP?x!a?>P2ENFzBEGgrF9ewv1Qf@lw~J^#n?&|n zzAqD{0S@SNP!cMXkbu`5L-<|$POQYKSu`x(~&bgO}M%5A4SE)ep zG$ka^19``NxZQC07>qyJ8S$H)xhG$3of4#>J&iUaWAx0hX%zkV?l{>Zqf&H|}brpBJyU}YU;oD6w% zXFm0C8b0Ey%Zf9=dJU#_k z#fH<$!RH2^Wyz$Ty%7;jkQypIQ+sLU+WTrYT1twJ2-zP0q5&DP(}qY$LKe7Dybpz4 z4AAF&JzFyB$_w0p;N;CKscPms=Q6r<^@Z2H>Skn56{qXkju%S#vf7K-?=! z>*Q|>P1mj9)4|JPt-3Qedd3!1(rZ|m0H;rUkg;F%*JoeP7D zJKY{EWw0M2O^PF)gXM;vh@`#;wvg~t-_SrPWC-&LRs`oiSA}<050&WR59cDBMSZ1c z8=)}-eS=r7@}UKzRsjINPkh`(_}E@4LioTxx(T_BGC^9C$gv13-lv)m zW-9Ph9st5(w-<&_LJ_$3m;w1&ad8h4I7Luo28)%z6l%4NGu@J${GGCfR?uo&;mu}x zF&ZWmu`zsMTLuQkg-l&1B-WTNwg$);Q!I-@Vp4c`*PtYILH(2;NaD&W`Hb%qq>1y# zy$?KXdL2g01RaYx_}-7k-&3o~s1bAPixG)~DjXfV*tpd9d;yFI%8S5(rwksH5WfZP zjWJK~7sEmhgg2*^Cril$(%#Vw_4}YQKQmzF$i&JHZnUv)03ecJ9jO;&tS?lp*G@TU z9@u{3KYbE;qac;^r7c%TBxYbu=1Nk&^Ww7hSkVeTEIPCjE(;23Iyg9d0B8{m$cWM9 zVZc^WPwpOcA6}Wz_)necZvZaUk`e2&F;7%jCU8>0Jym9HZ5@ovD2jfuLiM}YA#&3^ zwVnfhpv{usQ)JkNB898+&>a_nislh0YCTbPS<`tVxHyK6@S6TS)KSm_g$@E9`uz-- zU%rAjqGiMLGfQ%@1f8g)Tr@ZbRAQh?w^RuVF)4YDJh6#mU>86<|C+mIOf&H1(aOAc zN`A|zIp16XAh_5W<5dPUP-XmS0#};(a^5U}OjRZ&560$$U%kH0af!9gpk`-aHpM3< z-M^nJ$9#SFc#_6ov7ox>=ea1hixrfX8hibtN;!8mez#5%kP+zjmp)M&URZ10=EEXD z%$=l^e}I+hjA#3rlxW%nzGkS_dB3qmt;Rw>253}N@TYysN6A=vLK=@BCOYF7)9+RC zjLpsMy5xJ92&n&%qiJO))VwmKYpl*Ea^WUyv1ze$)KMLQ8q&1DU9&gKv2)g|8Q~^^ z5CdhfPahc;<0$e8sh_WO$u@U_OJ1@&?iF7ZfbKUoH*Ge)GN2FN=T7r zXU#zXS;c){CRs#=2;_O}q;2#h&&Jz?huq_5%b`;C@X=mJb$AdLTdwuj&*dUbeCxam ziHy{o0!~m^G)ef`9By_(ngU6Q$N9cn&DksmJ{4I|^?4zsn%cK@5Gf7?inYpEZ_qX+9*?Bt*6JGHY;Df)*J2_H*gCA0!kD}-pAEIVJ zrvdy)j&W!xWaUiJ5!htF*PGNkt>91U5{l@_w@S;S5^*A=-pUV&h$Q7!nsA>R8^8BP zl?BEHCEu!Jiq3LLr##SYY&_m$VPWPw-s0DV(-&q?qP@5&fR)y_<^WCnMxx@(=PDii z={|G%CZ?tU$Xf>H<*|W(k;u_6Q+IRksc^L5N6#0RlPi87=OB`Bf{=U0@k^amj`
GsCdMov2xkfawP$z0Iw<9#N0)RYKK_NvxB$9cwB#>0DUe3^@lfgv5Afv0Z=!AGk9rqss zAH@HC&Mm0b0~#><>MgrCQxtvbquQ%p)~pW|O zx!m1YhRAWCRDKwlOiE*3X8csv2H}T#X2Zfbx+<=fH!?T&^0AGbotVmkM$BfHsi}Ez ztQ2bN%`FZw(T#_B{c|qcoZmkG5I_=B<;{K;&NP*6&)Mp&F7^lqN82;d95^{82ywL= zTHQh;F=j*@-_%-pkT-JU#DQAm{1nU{qDFW3tH7!~yA?9Kqd`e$a0_$C9@VLx`w%RL zusJp4g^vfV_`Bc=HuvN9N>k4WM(dp~f!k~;TspAPrNyn4E+uUSIy~9-9Cp?g*JFlc zGQcyX^YC+HNy(c%+!$>2`qxWXwF|i{%d;og4wK z7&mze$)ziL3+du!VDMJ8WrO+$yq3P(sReKvn3M{vItWHh(=Q_Y}aYBPp3OP3bd+*60lTwYXtEUh!XTpk+S3F+V-^G^${+yS!H&HMG3R)Xv1UBFX34q5&6i00%z5NG zrNG-XnuRgrncT+HvqYJIIoS-Abg05Koi>_#-#Mj10eP5L(Qim0*=J-GdFz2C_`B5~ z^eo=>n$q~FC@XO~)~1KY#_hZfbT4nky(v|-wROR&UQQ)6sF5#W9HnJ_L`vf{36l|# z1;OJ8P0NN}XKbucc_?3f`{oc(^xmv5#0J`cjJD@BB(|K)sW`S$=~aM=h9^@p^zn@p zG6HebmLc1YP&M7xkew^suCM#hB>6DT)wQa5x*Hv2x*!*8=;=8~nmn&$VJq;9mGTQ` z-K*V)J-CxRJNn!*8P0LNKj*l;6K^s-8UG|&BPWN9dO4+!m)%-j4FBP1If(*njuekw zGz*Jd0tzaWM|gZ=6B=l=1aR7?Rehp1D>cQ-04j|M&$EC)42ZwuhG#Zp`Dq1PGwaa2 zh1Ono!)X7BnI&+xpcX-cn<%-Qtz|J*a}Dv_*f^|2P7eBJw=r-`v1>zuj+0KfJw}3~ z-c#DtAKUY6+4A(ErQU2C_~GEc)l2B)^r@Ab(zwvk6YajFHq^#%-)!f2_p;chCC-!j z=dDjte2U>T_TVrK;ov zYkPHkH$1H|WrW*rOS{JdDS0n*NNHJ_U;Qj;ZpEg6DkQ+FitM2eZsQXWTc|2#s~pCa zdo*pI*9cAP!GHt6)WC-UMt4(xBlWzVID>Wmoz|&CWmT!wd+74QwX@Fb>}XRYwVfw3 zBwV$=Dqecb-(1{3RFp`p=8-_ChfN9TO&iBhZ`iPN#bl*N!GUP}UMZjiQ0pxM-hs?A z1)XU8U}(2#j7e*>+NuKM`Aj*;YRNiuTyPWX9@E)n+MxMOp>-Xg{?o0!oUEOR?lw9UxvHzu)(&?{SA1> z7@=92WAelLMU)kdTJL>LKfJ+8Z=OF6ZoSs*yLKfK z&zGD#wIJRlg=`2??+HS8dActXTzhgWSKQrw>~6CPd;cZr`7?5PPGm5t=F{3a!Ae zQfssLq_9S3!2`-_I=8Sr!`AN9kH8YWH~Q`T;hugGsr`#jNc_CbP@9Q`t@UM0rY#F2 zi6hQKm#y~8!PA-w4C{Wqq=Rx3IGbHPYP0Xz9Vg4nivv|Mn$|D8oHUxn&{uX2aS8fm zp<4*DvHCF8(P#G1J)d+&#n9!|9@!!~lmXC@6we^n>3s=xB7}x_0diP@Dx_F1B4fWI z0}+ATO9R`ZRXLCSX^i3(7j}UCZW5vxEj{T=mW)c~g2{?FUKDSyBX$V2aMsPFE}^0Q zvQJtSHO^~ygXCA;WLv3rVCHuYJ4Ul)Y){ucZI-l~$Lp-O>s?0g;nP@i0I6V|QHgi( zYIbTCHloLLhPpZFc5r0U*R*u9zBShMnq8Fh z{U7dQUkqqq?b+LQG4x6EP4l$@1BW)zWeN)Oqwsqt$;N?(7w1}GaE`lKt&cB^zFsa2p_KdNz-fY z7^8>Xc_xz=H$Bl;wtm!!QdbOiehz4-!UW`mn6dH^Kgb7f=1NI@Y(-M^ zSx2C$h!#8;5oeUrBdKhA&!a{-@XF&`gj_Ea-(7lYOj38Tre54^3%&CGsR0IPrEdj& z`}U&5IVrhXI=z3PHDWR-$^AZU^xW2DL2JPGOO%4PSSjAcUJaM8JgDFoylLct3h5Q} z**#zQe||fGktU=3*#0*BxQ87Mol8nN!-Zt}ZBfju^SiTl##~*8uW1*@t9&4b&LR>x+S_ZAt*bv_O_(<76kLqMv*J zB;-eOb7t-bv$08-=Rln*UtkcjIb>`8G0c+yw2M*(tx!dp8?+lAHsydYS9+$5 zuN?DB24SZU=}kAbmFMQ>uVL;S66r`I`+o>;8cKoJzR>6W;O@`hZ~t4T_LZvH-B9;g zrm=YGbb_EooPDa%(V4T>zJV33(q~j&$3~YO1d?~+Ihw`2(D5uD$kJ=XtJkuT2Of+gqU71Fp*s0fumy{WU0fugZX?%AN=C zg@t3zOZVafYe)Rgrg(sSLB|xJ3Hk#c(72(gXulE)nyn-RT3Mj%stmB|EY{X`6tyHI z+(OG=0Y_(2Ep_Kg-Kx`Z%Z!mLrjS=kjHXrYUqK8Dxa$dRAOrS)~bFo;Q|ds1#Ma&r3wWPi@e*NcHI{` zb%hx$Ehk&{3vx}K9C*~*x6y-1m>+P-6H0%dKWcOzUILm6nB<15O5RStM3vZqKn&ZT zPV}HLUMba3CpfE}11Q-#?KF^X8}0iJflCZs*Ir*$_#6-2u-?5#^x*>$xCT=RbkqWR zeLyGBmvaUjJ6Zre(s)lVR?oi@37*X;M$+rq?61gD>Q9fq_NMO!;eoE#?K|h4usO!?fV$OcLzfZQ#GFTplK2~aRM1Sy7vG6I93kh7Q)S&9T-4_C}-w9L!;N_%GuU}XCc{;`I z5z`)hS@_*!jEft;N31Mprs9I(6u z%LQ7#F*fVZBtE<10n2`_PRabz5)Sw-rtZg>-6zA0m}|@>cgb*p+7JaGvXz~~JD|Lo z2BK!IG(YfAy>pp)I>1u(X&$^Bt>E%bsQ0YkR{itTMN8-CK0`z-$djO(i-lg-veAy7 zXE8TaktmuL1r=h8d<(?1~upq$C&M~f9 z?xE|WoHODrAS-L@nU`L_B3zU2Yhi|f0xxrzj9uxlONurreY8m>dD89T=?YFtaQDX1 z`;UIu25ALcy@oyG%_wi+#ecFTjpsn`5D|6zZ_Ko=kQnFR3>1`~l?s@x;!#JIuZ&}Q zW}2Iu*@7HJs^Lq()JNk|y^o*k443@)@HQa#$`O&48%wt8>O!q>wDHHlJaSr?6V|(O z$+gT>zBj@W5shUQpA{3HD(OT9d=%ixtN_AQ{Hg;3jyPc6=n3O}(b1`X@#ZZ-_`}<4 z9tFAD>WQ*iWN!ztAAb(2Fn?GlHepm}%Y^n*0686WWGidFM*@5C>$aeul`!@2&Ch(@ z#TlmmZ34Twb$~1kbO5n$W$&5w?>k-Sa%qBACH;fyVavW9rIA8lUUyVf)tXI zQ$!*83lnfS;2y<+sa2Q?;*&0PP63Mn-jn{3Kw~1{A&p{+(!d@}lU>h1kHg<@-r9qgpkdH! z;oaHWOJ`%l<~nw8S2f{JHYlt0$np4b;DQ8UV!ks5t{2F|fB%0syCtIk8T^bkuK%xm z>;D^X`~U0T`Nuo|AFF-&>5#Z~i=SxukAd)R1e0+jX0x>o>G^#48^4WvH8XmFD8?_E zvTxTfP%zf}gSM?2r9I_SMz;}uyw*4S#0kg#x;pcs4pd}_09_-4t1DE|`MHC~%8Fv) z?=G)Fnuf0hY#Zx^+V!sNc-O}jodU`nU8cnp$0l|2gNt$a@9t`7xO{l0bW{WHa_YKW zlm6=WD~2p_;<=ullonTZ(}aXvN2eZy>mrsMXUJ6W6@F;_T$EW=6;30{Tv_gr01P8y zOe^RAQf}ewm7XV#kYAVHwcHratRyDt1y8w___*e|o!#@!-m$T*rj6Zp@g{?+R%k}w z2Nm=u1|HY~wcr^o(Qry=!=$;=JYPM5FVyb(fLgD@>|K#vw#rmEaT?#4nQ=_@%@y&` z1xiWVg19P^(X=m4ZG`zcMCctAI6wb^q4O=hvq*ERjfY6V_MbZ)N7tLB9z%2#ZI=&L zmXaLpIvABT9~`9vMnFNF(8G^!3?6vV1aKnCr1%|iTB?4 zUi(g+ou$qp#=)D ziht)J7b99Vo6Q6LIY|Qx4Agej8lxkS=Qiva7cxcJzUME;%Pr#On|BmX^W!$)n@2Cf z65xMS#?0oeh?wbqt9TYhZK14b#@O3>QC_L;`1}qp7uQvzq?C1+I1+r5q$F;ieHGhs zqtKTiPGrG#IRiTz%fL{iMzQh{^7@5GESS;u4x1n$ z1mWl6))i6FJ`r4Ze0=WEy2_-EY-bi@Vv1j0!GvVk*kI4bRknv=F0@D=9u9pjzBx8r zk~!yQU}q;i*@L;@;D=XNk&4P)MSQ#1fqf(AvfNx`XYl^p3OXn(I$ z)HcB{RM>eA^VK*#F?qC=MRN@dr4Fh`O=| zCccH1ls%n-Kg3ToCSp_s84*G!(n28dFiV=+Do%!t0Vd1+ z(5TSSG2)3L=B$p+Ak&P{10AE)&z~@3O^MbMH1nXm_z3qEnxx4e%iO+)Pa8C|-k*HC zzo0PN-`Qz@!-q|b$wnIR`F?0pP0gq*o%hbTh3>B13JoKnz5_1^4^Ak@V?+?WR4f*0 z@O91Ts?dv0ObGklq9>G*|f_W6h7JV`&EHIi+ortY=<#S1ih)8j&?nxl8x&l@ix z*~uy~<(M<>b-+<=SRMiY+w;(c-cdOmX*QKsos?>$>WQ`i?Nd5YdrUr_sqOA zTZy?el>64limukVikh3PesgsQp;F&)kiBm_c>Hi{3jzU&3AWp`8Sgs4hu(Il_n%o) zSYpU8uV&{LR+D83zsNc3z$lWEvfTcQ+4lw~_q|4bXkIyy4b^=?e?q9IC-=>%jTLj| z+1Xx(7;u_g@qK#q)dh!`TDBP}T&8nxwt2OLoeP`)MUbQmt1Hoe?j9gjo%?;q_xV?C zpYham{Hn~%>0C#wE9Rq%&BVv7)pb$2dWq|GSGT^xqA?&$FPMyn?ikQDGR#p@-oi4k zo;8&?kuJ|o)oyg@+F@c?(9l|ZmFvn+E-Pk|kW>BA!JyAzfr<=G5st!2GKruezSiiL$*_J*$uw>=ErnyH*04WM!9*vyjP3Y6O#RCpWYcF#rXH;klCoQA*V(>u{mYj%xsE@ z;XNpvY74v-mdG!UrYPy(Z6)#M>T|#E_ex4+*}T4};u0clCMR>*2*u7Rs-T|OeezVL zr;j2(ZfR%B5#t|CNxW)`krOmP#qg|wM4Fx5)p7cP2PJj>;?X*W5)wc1 zKSsw%@>mHMh9p{fd?mSU*^ z{gpV)g7HPBu8E%%1jqdbj4x8SMH$|eY!Q{&E(cO{h>c!i(3;`1)XFy3Nq!GX3YMj| ziztFZ4?04r!@~s%%I9uR1C@ASS9Z({1p5BXqruyS8tc*KCx~>ynfiPXXSu{Of|hak z7aj5d!wm0O@I-!(j{e#5m~&N9o|y9@o&-XIi4U{ep`m^0XJ%YhnaL`$P4BqjAyvyH zU9h`7?u=s@{GiyQ!(2bgePf+G4|0B1ptrsK=DhueJWR$NbzrH)(i{_`Rr8uL%-~xH zcx+HCu^ox2KP|{|2VOYY|9-{MaxK-x>`&lHFP`T&CzWfe<|PI~hagkuZA3jn@v=mJ zZ4j=u0!87V^nYP9cl7_JRNysW@q}T0c|8Sbe?BnCT#su0a@2}qsjGoCfN4n}^>X`e z@}aR%|HL?)a*1DF?!1WM#X|IsY}&+IDOb11G!zyP`3$xZD_o1u zhWsI<*J=)ezM8Cv4s;O&#Y|yu&O4nD6N|prRzf^_&jq~EqUR6Bo&Q8MWe4{*h&rf8 z(I&MVQ;E1qvSkMu2R|JhEt+W{?Vm8nZ~R<~cL$xUa0`bRCrja4QV!JAph(f&u{Bds zDLPxgR+d9$XbW(Gjgk2D>C|+ET1xj%Tz7be(E8i48)L$SsHRp%bR=Ca#w|2^++*nK zieQolk7?<}j72z8m&*rb+`l;^=J4`T4UMpB&;zNIUYp&sG*@=s*;lz553N2L97 z=RfQ-Cn*hCmTTZglOz5&nb%|4ShL9s2*;^9LTeL~Q*+PXAO` zIlGU3nVA!{Zglbya>Mr$H;Z=9U42@=78sA+^v6l>f9 zuOa4|6LGqjLg5&pM9ZJCc_5;U~Qq zPOipS7F2b+aqau$h?zlDtwnI8lJl-&-1^w)=d2Il46E{z#Wf~mFAYj>USoG_1a;t^ zEH6X3ZneCD2QmcY z99vi4{V={YE+ll-NN2!yt@v9+P3og?Q79DrStvZo5O3g{nVFF9qAExaQN2zxBMtuI z{fJ=Z-^jq|doy*Z>bGZ7lf~yF<}+JYy*A>4DL%jbRwi@}BYwJa@X=5|ccZj|#ASf5 z3zlqEy*=C_lYfxe3Tq!UtI4Y(?JwX=!2Q9PpC8L4k8fBOR+h~iw$lK!ovBRXecHwI zHghh-N(boxotAFF9cf?Y+pC))IS#(zGR5|2QdlMV^-_d8U|Uq8ryHj;fz=Vc&+PWC zttt`s+K+9{ zxV5yxSP9~eG;Y?5!I+E&1*XhJlW6e0BgP8&Jml^Ix2o#Tu!A|*zAA?mBkTbzHWPF zRIt8-1&Kfew`*%kJhkKc=C>Q@X~)IWeo4lPLf}gW2j_2k;}A!;G#Q^Z9#07+mOr~S zohp2eaF=3)bT}Yna^FuOm_Xeo1hL`+&vq7h3C(FoT?`9gXE@DtU6qe1d`^z;kVV>k z&Ts&@64pOWtKf}z>A?W-Bw#f2BuuhPtG=@To-~LR1Q0L3vU5*RZ2a<}*Y!0gF|wBIm(y}1&=v+%6bzvtf<*r`u@@o9O4UrU{ynZS+-VV z`Gt7K#E@}rzLxXEBW%Ae$^Ov<4{xzDfu{;Dq$S!`?{@z9qBC`VZhfi7tiVCGu2dIK z^5)_;Bdi#XMpuRk7-YyG`d6vy?OGgRvB}BW#MIx_#kT4LfBLPC9At^Z*8d{|0z%*h{iBKKP!2J1a1#ym=a@_fK;`%x)7Q5 z^c0ylQ(=fB2>{GpF!#bCRN1VuDVWSiwmUKBJw>%!Ep8LuEDylzqC+58lFO^aG~Avm ztBWq#F5SaOX5lzSr4uFM+#qnZrY|VWs-3Z;vIhtALQZMg7hA=p;U$gF8oW(DFh!PS zXFr0MWc@L|awT*JRG`w!oqmG;r>#!$bwgm=6I;7s$Ovj)h$lTAKl|i)z6K?;?Z2#A;uZUYO;DlBL3gr$s)|ZH7#BX<~5N+QUM0ALa ze<#vY{ktSGAAtmr_=;n2r2TPQds}vRY%+C|zyB7&>MR2chE142fU@X_6;Crm-b=Dp z&(3nJla=L6U_;FrPG9W8dIz2~WJgQ0)+-sM0E>lGbc;6%}-7P%X*iE$2K zP9vG#c%F-5R+v(5Zd&cn@c|8eJ|bBJ){BJbWKvf*{Lk?w8u)a{;#mYzN`0r(w+M_Z zsc3sJtqL@s86g|U(FxPH5=F~)%R;L@qFP2dBFNOFbC!^NFtZN|H}<^x_z<@AG(94_ z%r6>SRc8OTB6EP2(d+VUh}~|u5Iz+R-!76|XL{E0z(5*eFBw3CRxzLhUv%;%7qDzj z8d{yNHtDlUq@`my;|#|qZ&?d*I!{4TaO((_Jl`P$$lC@!v$`I?vND_bnK0tvNlVD; zjzff&MbKddo@fcMW4G*JrhC~zJV$w(H<`0;r(YXGjdo?!5bmpVd?>RfiZg+h zkX}aV2<(Ee;2V0etgMFttBeWAmQ`6)#!szW5RQQr+`{BZ-YY9z6O)tn!*_G)qdyzG zD-mc{9tgel@cJ))f(@uPT`vY{5}cyh2G?s{7IK!Q4iAo61F&db>FxjI(i;EGx=>cH z?&qNwcqO_a?i5x~h)pIfjaCi89#S1^3*1W+9Vq&yk*@$=pG&p~Zip;_;H581)CJsh zeoWT{U*9Bsv~?Zo%!FqVM2p@;H5R)cL6RA9Tf*rLKFt-3{$=TYhZcZ^g%_Oi>-@a1 zVr=FJU3G#m|LE6v*=Lt$;pWLBHf2bYs(^~df3V@%e5V3RNQ+qJxw~-nPAj#6hQ=G8 zOIL`z$qP=pI!A>cSE%$O^_TE|#fA2pGUk#gdUPtHCrrRRIy|RnJ?F`=wkEl#2u{t= zoCrkWD%40$@bkY=8VdCbJm#wS7GcaOoO<-kYlYw&6dKt9lVyPnVH3%h8sM|WV0EBW+cF9~Q9}B?Z90-D0`f-ZuFWj*`wrU@`&9v}|r}bb9wI z4rO)_p8gYjQ~m>b1mxebaO^RSjqwCZy>xZ2MeprO`inK;;pL3k=X@M^+CSyv+v)UI z8E9l(&o6z@j9Jgg2}mhwfY$fRUDJ@MsdZl-S9FO@6GLxT#S#LZ5eYQBaVIDH+3)zj zHz!z6v;5~LxZ*LfvJVSta^!CeDual_>ktSRkey)J=O2AXK3pr?V-1)}9#F|o&HVW;htwf_0s?AtQG{zeJYhhw1N={ZTT^af z{0has)rr0EAV>xhsF{`KxjFB@{T&^2B?wk2GHmRc$>mS+kgC|B9Cz1LlS$O+LYyq# zYQ$}BdOEGv*Nnz-%xagV6?Atp0YETnZqA<`EFC3;E>V2{UI`}Wp!fev5g>EsE|0LH z6=#rQIf5^%M*M&7rM}}J8`~s%8{x0NcJJ4&>`|I1K1k^z+X$#-j%|@}{af6x6}|O^ni>s z4$7HW1GoZsY;@MMmX?<2>rt?_vvV_LJr#C0MFu%Xl1C*U6Y08ZJ_$FoWO=aLMsEdH z-b7BW$k=$~V>?NhO!8gT^CuZYLo!^ zLPLk2%)tbZu344e4wGHMXKo%Dvawx;=AjzJBEGQnM)4{_lxgegkkWedZl2HaEA)~A zY%8_r%dJk7wVN}~KEcSWI6(HhlX(H2%+j0?)tUh4)=gxpEWs1_MO9ojdW72JMT zp4{o_tX1D5-4!uGYk)tXqV3O7hCj38P}{=L9*grz{W{`sCD;Bpk4JKH{yg#~%3ZN5 zM5zp<1Feh2I3P8J0+}IkXh=&;EN%NyzCO>WzNzC$nUC1c4iK)Fa6BxIQF%L9>Tt^}xu$KJ?XK<(b2+^hTEGmALB+Ld#k zoJ0xN|MRCo@<5qe^EVI`>*|HwVj3%CcN*hNfaleqwtcU5pF>i7FD07%Et1V!z4$cS_^Bnym8$=@B8NKsc&pg2sHgxq<_+ zG$WzAvy+=(V{jlGh~y)>J%UT4{?`Es<`!6>K>@(yM3LP{B!}?P-X?Wczngn()U;$z zx8)@e2LWx3_nkCDzhY$dbDW#8?DtV5ssNUL(UgmQFL#Lnl+y;J`UhU1Uc=!<-Z_G` zWfmd7rlrS6UF3|=ZJ$Yt{Jr0XA8-D?)2gqBfr8d>my149+qKkSVUS#tW%Z+)B{ReO z_=_E-i=PSSjm;cnK%IS9fUWybYWIio1Vo!}(1-(Y6vEuVW zo;-P3zF4&Nv)1FXAa*YYTS8qMR_U&Pu%Fl8IcZBBwb{^%fS$6%AOqZgSY=U%JuKYJ zEYJAwa5;M#{+&c5x-*Ub5!v`ZBWc;#-u?gf?@PJIL9{;zI}_X>;OCKwhH|;0W!V1! DZ74A_ literal 0 HcmV?d00001 diff --git a/gatewaybench/analyze/ttft_vs_memory.png b/gatewaybench/analyze/ttft_vs_memory.png new file mode 100644 index 0000000000000000000000000000000000000000..aca6c82f0157b04b4326b2d2db26a53ebf0e3af3 GIT binary patch literal 87310 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 literal 0 HcmV?d00001 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"), + } +}