diff --git a/README.md b/README.md index c1290b79..99c24ddd 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,19 @@ And you think to yourself, can't I move faster by focusing on higher-level objec **High-Level Sequence Diagram**: ![alt text](docs/source/_static/img/arch_network_diagram_high_level.png) +```mermaid +sequenceDiagram + participant dotcom + participant iframe + participant viewscreen + dotcom->>iframe: loads html w/ iframe url + iframe->>viewscreen: request template + viewscreen->>iframe: html & javascript + iframe->>dotcom: iframe ready + dotcom->>iframe: set mermaid data on iframe + iframe->>iframe: render mermaid +``` + **Jump to our [docs](https://docs.archgw.com)** to learn how you can use Arch to improve the speed, security and personalization of your GenAI apps. > [!IMPORTANT] diff --git a/arch/Dockerfile b/arch/Dockerfile index 7f933da5..98a113ad 100644 --- a/arch/Dockerfile +++ b/arch/Dockerfile @@ -29,4 +29,4 @@ RUN pip install requests RUN touch /var/log/envoy.log # ENTRYPOINT ["sh","-c", "python config_generator.py && envsubst < /etc/envoy/envoy.yaml > /etc/envoy.env_sub.yaml && envoy -c /etc/envoy.env_sub.yaml --log-level trace 2>&1 | tee /var/log/envoy.log"] -ENTRYPOINT ["sh","-c", "python config_generator.py && envsubst < /etc/envoy/envoy.yaml > /etc/envoy.env_sub.yaml && envoy -c /etc/envoy.env_sub.yaml --component-log-level wasm:info 2>&1 | tee /var/log/envoy.log"] +ENTRYPOINT ["sh","-c", "python config_generator.py && envsubst < /etc/envoy/envoy.yaml > /etc/envoy.env_sub.yaml && envoy -c /etc/envoy.env_sub.yaml --component-log-level wasm:debug 2>&1 | tee /var/log/envoy.log"] diff --git a/arch/arch_config_schema.yaml b/arch/arch_config_schema.yaml index 59276589..467d6d22 100644 --- a/arch/arch_config_schema.yaml +++ b/arch/arch_config_schema.yaml @@ -90,6 +90,8 @@ properties: - https http_host: type: string + usage: + type: string additionalProperties: false required: - name diff --git a/arch/envoy.template.yaml b/arch/envoy.template.yaml index cac17187..8144a65d 100644 --- a/arch/envoy.template.yaml +++ b/arch/envoy.template.yaml @@ -29,6 +29,66 @@ stats_config: - 180000 static_resources: listeners: + + - name: ingress + address: + socket_address: + address: 0.0.0.0 + port_value: 9090 + traffic_direction: INBOUND + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + generate_request_id: true + tracing: + provider: + name: envoy.tracers.opentelemetry + typed_config: + "@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig + grpc_service: + envoy_grpc: + cluster_name: opentelemetry_collector + timeout: 0.250s + service_name: arch_gateway + random_sampling: + value: 100 + stat_prefix: ingress + codec_type: AUTO + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: "/var/log/access_ingress.log" + route_config: + name: local_routes + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + headers: + - name: "host" + string_match: + exact: router_model_host + route: + auto_host_rewrite: true + cluster: router_model_host + + - match: + prefix: "/" + route: + auto_host_rewrite: true + cluster: bright_staff + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + - name: ingress_traffic address: socket_address: @@ -615,6 +675,38 @@ static_resources: port_value: 11000 hostname: arch_internal + - name: bright_staff + connect_timeout: 0.5s + type: LOGICAL_DNS + dns_lookup_family: V4_ONLY + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: bright_staff + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: host.docker.internal + port_value: 9091 + hostname: bright_staff + + - name: router_model_host + connect_timeout: 0.5s + type: LOGICAL_DNS + dns_lookup_family: V4_ONLY + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: router_model_host + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 34.30.16.38 + port_value: 8000 + hostname: router_model_host + - name: arch_prompt_gateway_listener connect_timeout: 0.5s type: LOGICAL_DNS diff --git a/arch/tools/cli/docker_cli.py b/arch/tools/cli/docker_cli.py index 6edfb8dc..d12354eb 100644 --- a/arch/tools/cli/docker_cli.py +++ b/arch/tools/cli/docker_cli.py @@ -49,6 +49,7 @@ def docker_start_archgw_detached( port_mappings = [ f"{prompt_gateway_port}:{prompt_gateway_port}", f"{llm_gateway_port}:{llm_gateway_port}", + "9090:9090", "9901:19901", ] port_mappings_args = [item for port in port_mappings for item in ("-p", port)] @@ -56,7 +57,7 @@ def docker_start_archgw_detached( volume_mappings = [ f"{logs_path_abs}:/var/log:rw", f"{arch_config_file}:/app/arch_config.yaml:ro", - # "/Users/adilhafeez/src/intelligent-prompt-gateway/crates/target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins:ro", + "/Users/adilhafeez/src/intelligent-prompt-gateway/crates/target/wasm32-wasip1/release:/etc/envoy/proxy-wasm-plugins:ro", ] volume_mappings_args = [ item for volume in volume_mappings for item in ("-v", volume) diff --git a/arch_config.yaml b/arch_config.yaml new file mode 100644 index 00000000..90778670 --- /dev/null +++ b/arch_config.yaml @@ -0,0 +1,20 @@ +version: v0.1 + +listeners: + egress_traffic: + address: 0.0.0.0 + port: 12000 + message_format: openai + timeout: 30s + +llm_providers: + - name: gpt-4o + access_key: $OPENAI_API_KEY + provider: openai + model: gpt-4o + default: true + + - name: ministral-3b + access_key: $MISTRAL_API_KEY + provider: openai + model: ministral-3b-latest diff --git a/crates/Cargo.lock b/crates/Cargo.lock index b585ef6e..fffb59a0 100644 --- a/crates/Cargo.lock +++ b/crates/Cargo.lock @@ -17,9 +17,24 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli", + "gimli 0.28.1", ] +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli 0.31.1", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "ahash" version = "0.3.8" @@ -82,9 +97,15 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" version = "0.2.14" @@ -102,12 +123,33 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line 0.24.2", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bit-set" version = "0.5.3" @@ -151,7 +193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.8", "serde", ] @@ -169,15 +211,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.1.30" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", @@ -196,6 +238,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "2.34.0" @@ -228,16 +279,32 @@ dependencies = [ "log", "pretty_assertions", "proxy-wasm", - "rand", + "rand 0.8.5", "serde", "serde_json", "serde_yaml", - "thiserror", + "thiserror 1.0.64", "tiktoken-rs", "url", "urlencoding", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpp_demangle" version = "0.4.4" @@ -289,7 +356,7 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.28.1", "hashbrown 0.14.5", "log", "regalloc2", @@ -487,7 +554,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -542,6 +609,34 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "eventsource-client" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75810b04951eb0b44bd2800345f6ee15321be90894568bd93aaef9abad1b646c" +dependencies = [ + "base64 0.22.1", + "futures", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "hyper-timeout", + "log", + "pin-project", + "rand 0.8.5", + "tokio", +] + +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -558,6 +653,12 @@ dependencies = [ "regex", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fnv" version = "1.0.7" @@ -570,6 +671,21 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -627,6 +743,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -648,6 +775,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -696,7 +824,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -710,6 +850,18 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "governor" version = "0.6.3" @@ -724,6 +876,44 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.8.2" @@ -793,6 +983,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -804,6 +1005,178 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.1", + "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 = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.9", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.21.12", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.6.0", + "hyper-util", + "rustls 0.23.26", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.2", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.32", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.6.0", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -919,7 +1292,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -960,6 +1333,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "itertools" version = "0.12.1" @@ -1004,6 +1383,16 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1018,9 +1407,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.161" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libm" @@ -1058,18 +1447,18 @@ dependencies = [ "common", "derivative", "governor", - "http", + "http 1.1.0", "log", "md5", "proxy-wasm", "proxy-wasm-test-framework", - "rand", + "rand 0.8.5", "serde", "serde_json", "serde_yaml", "serial_test", "sha2", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -1097,6 +1486,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "md5" version = "0.7.0" @@ -1118,12 +1516,61 @@ dependencies = [ "rustix", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + [[package]] name = "more-asserts" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -1133,12 +1580,32 @@ dependencies = [ "hashbrown 0.8.2", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nonzero_ext" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1166,6 +1633,144 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "openssl" +version = "0.10.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +dependencies = [ + "bitflags 2.6.0", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "opentelemetry" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "opentelemetry-http" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46d7ab32b827b5b495bd90fa95a6cb65ccc293555dcc3199ae2937d2d237c8ed" +dependencies = [ + "async-trait", + "bytes", + "http 1.1.0", + "opentelemetry", + "reqwest", + "tracing", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d899720fe06916ccba71c01d04ecd77312734e2de3467fd30d9d580c8ce85656" +dependencies = [ + "futures-core", + "http 1.1.0", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c40da242381435e18570d5b9d50aca2a4f4f4d8e146231adb4e7768023309b3" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", +] + +[[package]] +name = "opentelemetry-stdout" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e27d446dabd68610ef0b77d07b102ecde827a4596ea9c01a4d3811e945b286" +dependencies = [ + "chrono", + "futures-util", + "opentelemetry", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afdefb21d1d47394abc1ba6c57363ab141be19e27cc70d0e422b7f303e4d290b" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry", + "percent-encoding", + "rand 0.9.1", + "serde_json", + "thiserror 2.0.12", + "tracing", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1186,7 +1791,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1201,6 +1806,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1297,19 +1922,42 @@ dependencies = [ "common", "derivative", "governor", - "http", + "http 1.1.0", "log", "md5", "pretty_assertions", "proxy-wasm", "proxy-wasm-test-framework", - "rand", + "rand 0.8.5", "serde", "serde_json", "serde_yaml", "serial_test", "sha2", - "thiserror", + "thiserror 1.0.64", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -1331,7 +1979,7 @@ dependencies = [ "cfg-if 0.1.10", "lazy_static", "more-asserts", - "rand", + "rand 0.8.5", "structopt", "wasmtime", ] @@ -1354,6 +2002,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -1361,8 +2015,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1372,7 +2036,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1381,7 +2055,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", ] [[package]] @@ -1419,9 +2102,9 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -1445,8 +2128,17 @@ checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1457,15 +2149,80 @@ checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.4.9", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-rustls 0.27.5", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1491,6 +2248,94 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.103.1", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" version = "1.0.18" @@ -1506,18 +2351,60 @@ dependencies = [ "sdd", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sdd" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -1529,29 +2416,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.130" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610f75ff4a8e3cb29b85da56eabdd1bff5b06739059a4b8e2967fef32e5d9944" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -1568,6 +2455,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -1603,7 +2502,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -1617,12 +2516,30 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -1647,6 +2564,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "spinning_top" version = "0.3.0" @@ -1698,6 +2625,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1711,15 +2644,24 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 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.1" @@ -1728,7 +2670,28 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -1737,6 +2700,19 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -1761,7 +2737,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -1772,7 +2757,28 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", ] [[package]] @@ -1782,7 +2788,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c314e7ce51440f9e8f5a497394682a57b7c323d0f4d0a6b1b13c429056e0e234" dependencies = [ "anyhow", - "base64", + "base64 0.21.7", "bstr", "fancy-regex", "lazy_static", @@ -1800,6 +2806,99 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tokio" +version = "1.44.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls 0.23.26", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.19" @@ -1834,6 +2933,139 @@ dependencies = [ "winnow", ] +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "percent-encoding", + "pin-project", + "prost", + "tokio-stream", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd8e764bd6f5813fd8bebc3117875190c5b0415be8f7f8059bffb6ecd979c444" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -1870,6 +3102,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[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.4" @@ -1905,6 +3143,18 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" @@ -1917,12 +3167,101 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[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.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + [[package]] name = "wasm-encoder" version = "0.212.0" @@ -1983,7 +3322,7 @@ version = "23.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe501caefeb9f7b15360bdd7e47ad96e20223846f1c7db485ae5820ba5acc3d2" dependencies = [ - "addr2line", + "addr2line 0.21.0", "anyhow", "async-trait", "bitflags 2.6.0", @@ -1992,7 +3331,7 @@ dependencies = [ "cfg-if 1.0.0", "encoding_rs", "fxprof-processed-profile", - "gimli", + "gimli 0.28.1", "hashbrown 0.14.5", "indexmap", "ittapi", @@ -2049,7 +3388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dff4d467d6b5bd0d137f5426f45178222e40b59e49ab3a7361420262b9f00df" dependencies = [ "anyhow", - "base64", + "base64 0.21.7", "directories-next", "log", "postcard", @@ -2071,7 +3410,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -2097,11 +3436,11 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli", + "gimli 0.28.1", "log", "object", "target-lexicon", - "thiserror", + "thiserror 1.0.64", "wasmparser 0.212.0", "wasmtime-environ", "wasmtime-versioned-export-macros", @@ -2117,7 +3456,7 @@ dependencies = [ "cpp_demangle", "cranelift-bitset", "cranelift-entity", - "gimli", + "gimli 0.28.1", "indexmap", "log", "object", @@ -2201,7 +3540,7 @@ checksum = "a2bde986038b819bc43a21fef0610aeb47aabfe3ea09ca3533a7b81023b84ec6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2212,7 +3551,7 @@ checksum = "beb1abdc26ddf1d7c819ea0fcbfccb0808410549d28bb3154c9bdb7d11fbcc58" dependencies = [ "anyhow", "cranelift-codegen", - "gimli", + "gimli 0.28.1", "object", "target-lexicon", "wasmparser 0.212.0", @@ -2255,6 +3594,55 @@ dependencies = [ "wast", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +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 = "whitestaff" +version = "0.1.0" +dependencies = [ + "bytes", + "common", + "eventsource-client", + "eventsource-stream", + "futures", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry-stdout", + "opentelemetry_sdk", + "reqwest", + "serde", + "serde_json", + "serde_yaml", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tracing", + "tracing-opentelemetry", + "tracing-subscriber", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2294,7 +3682,7 @@ checksum = "a666bf2cdb838e68b9b8370d7ebf8806b87ccc0d89a634bfc9ed8ffca1f19591" dependencies = [ "anyhow", "cranelift-codegen", - "gimli", + "gimli 0.28.1", "regalloc2", "smallvec", "target-lexicon", @@ -2303,13 +3691,48 @@ dependencies = [ "wasmtime-environ", ] +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +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", + "windows-targets 0.52.6", ] [[package]] @@ -2318,7 +3741,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2327,14 +3750,30 @@ 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", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -2343,48 +3782,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.6.20" @@ -2394,6 +3881,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "wit-parser" version = "0.212.0" @@ -2450,7 +3946,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "synstructure", ] @@ -2472,7 +3968,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2492,10 +3988,16 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerovec" version = "0.10.4" @@ -2515,7 +4017,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] diff --git a/crates/Cargo.toml b/crates/Cargo.toml index 3ba99280..fefc3d50 100644 --- a/crates/Cargo.toml +++ b/crates/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "2" -members = ["llm_gateway", "prompt_gateway", "common"] +members = ["llm_gateway", "prompt_gateway", "common", "whitestaff"] diff --git a/crates/common/src/configuration.rs b/crates/common/src/configuration.rs index 2065b1aa..4518e368 100644 --- a/crates/common/src/configuration.rs +++ b/crates/common/src/configuration.rs @@ -166,6 +166,7 @@ pub struct LlmProvider { pub endpoint: Option, pub port: Option, pub rate_limits: Option, + pub usage: Option, } impl Display for LlmProvider { diff --git a/crates/common/src/consts.rs b/crates/common/src/consts.rs index e58bebde..1ea0063d 100644 --- a/crates/common/src/consts.rs +++ b/crates/common/src/consts.rs @@ -27,3 +27,4 @@ pub const HALLUCINATION_TEMPLATE: &str = "It seems I'm missing some information. Could you provide the following details "; pub const OTEL_COLLECTOR_HTTP: &str = "opentelemetry_collector_http"; pub const OTEL_POST_PATH: &str = "/v1/traces"; +pub const LLM_ROUTE_HEADER: &str = "x-arch-llm-route"; diff --git a/crates/common/src/routing.rs b/crates/common/src/routing.rs index f4baf896..f8e6146b 100644 --- a/crates/common/src/routing.rs +++ b/crates/common/src/routing.rs @@ -2,6 +2,7 @@ use std::rc::Rc; use crate::{configuration, llm_providers::LlmProviders}; use configuration::LlmProvider; +use log::info; use rand::{seq::IteratorRandom, thread_rng}; #[derive(Debug)] @@ -29,6 +30,8 @@ pub fn get_llm_provider( ProviderHint::Name(name) => llm_providers.get(&name), }); + info!("selected provider: maybe_provider: {:?}", maybe_provider); + if let Some(provider) = maybe_provider { return provider; } diff --git a/crates/llm_gateway/src/lib.rs b/crates/llm_gateway/src/lib.rs index f585ba0e..e3a071a3 100644 --- a/crates/llm_gateway/src/lib.rs +++ b/crates/llm_gateway/src/lib.rs @@ -3,6 +3,8 @@ use proxy_wasm::traits::*; use proxy_wasm::types::*; mod filter_context; +mod llm_routing; +mod llm_routing_consts; mod metrics; mod stream_context; diff --git a/crates/llm_gateway/src/llm_routing.rs b/crates/llm_gateway/src/llm_routing.rs new file mode 100644 index 00000000..9f183155 --- /dev/null +++ b/crates/llm_gateway/src/llm_routing.rs @@ -0,0 +1,106 @@ +// use std::rc::Rc; +// use std::time::Duration; + +// use common::api::open_ai::{ChatCompletionsRequest, Message}; +// use common::configuration::LlmProvider; +// use common::consts::{ARCH_INTERNAL_CLUSTER_NAME, ARCH_UPSTREAM_HOST_HEADER}; +// use common::errors::ServerError; +// use common::http::{CallArgs, Client}; +// use log::{info, warn}; +// use proxy_wasm::traits::HttpContext; +// use proxy_wasm::types::Action; + +// use crate::llm_routing_consts::SYSTEM_PROMPT; +// use crate::stream_context::{CallContext, StreamContext}; + +// pub trait Routing { +// fn route(&self) -> Action; +// } + +// impl Routing for StreamContext { +// fn route(&self) -> Action { +// let usage_based_providers = self +// .llm_providers +// .iter() +// .filter(|(_, provider)| provider.usage.is_some()) +// .map(|(_, provider)| provider.clone()) +// .collect::>>(); + +// info!( +// "usage based providers found: {}", +// usage_based_providers +// .iter() +// .map(|provider| provider.name.clone()) +// .collect::>() +// .join(", ") +// ); + +// if usage_based_providers.is_empty() { +// self.set_http_request_body( +// 0, +// self.request_size.unwrap(), +// self.request_body.as_ref().unwrap().as_bytes(), +// ); +// return Action::Continue; +// } + +// let llm_routes_str = r#"- name: gpt-4o +// description: simple requests, basic fact retrieval, easy to answer +// - name: o4-mini() +// description: complex reasoning problem, require multi step answer"#; + +// let chat_completions_request_messages_str = +// serde_json::to_string(&self.chat_completion_request.as_ref().unwrap().messages) +// .expect("failed to serialize llm routing request messages"); + +// let system_prompt_formatted = SYSTEM_PROMPT +// .replace("{routes}", llm_routes_str) +// .replace("{conversation}", &chat_completions_request_messages_str); + +// let message = Message { +// role: "user".to_string(), +// content: Some(system_prompt_formatted), +// model: None, +// tool_calls: None, +// tool_call_id: None, +// }; + +// let llm_routing_request = ChatCompletionsRequest { +// model: "cotran2/llama-1b-4-26".to_string(), +// messages: vec![message], +// tools: None, +// stream: false, +// stream_options: None, +// metadata: None, +// }; + +// let llm_routing_request_str = serde_json::to_string(&llm_routing_request) +// .expect("failed to serialize llm routing request"); + +// let headers = vec![ +// (":method", "POST"), +// (ARCH_UPSTREAM_HOST_HEADER, "gcp_hosted_outer_llm"), +// (":path", "/v1/chat/completions"), +// (":authority", "gcp_hosted_outer_llm"), +// ("content-type", "application/json"), +// ("x-envoy-max-retries", "3"), +// ("x-envoy-upstream-rq-timeout-ms", "5000"), +// ]; + +// let call_args = CallArgs::new( +// ARCH_INTERNAL_CLUSTER_NAME, +// "/v1/chat/completions", +// headers, +// llm_routing_request_str.as_bytes().into(), +// vec![], +// Duration::from_secs(5), +// ); + +// if let Err(e) = self.http_call(call_args, CallContext {}) { +// warn!("failed to call LLM provider: {}", e); +// self.send_server_error(ServerError::HttpDispatch(e), None); +// } + +// Action::Pause +// } +// } diff --git a/crates/llm_gateway/src/llm_routing_consts.rs b/crates/llm_gateway/src/llm_routing_consts.rs new file mode 100644 index 00000000..9f4045c5 --- /dev/null +++ b/crates/llm_gateway/src/llm_routing_consts.rs @@ -0,0 +1,32 @@ +// pub const SYSTEM_PROMPT: &str = r#" +// You are an advanced Routing Assistant designed to select the optimal route based on user requests. +// Your task is to analyze conversations and match them to the most appropriate predefined route. +// Review the available routes config: + +// # ROUTES CONFIG START +// {routes} +// # ROUTES CONFIG END + +// Examine the following conversation between a user and an assistant: + +// # CONVERSATION START +// {conversation} +// # CONVERSATION END + +// Your goal is to identify the most appropriate route that matches the user's LATEST intent. Follow these steps: + +// 1. Carefully read and analyze the provided conversation, focusing on the user's latest request and the conversation scenario. +// 2. Check if the user's request and scenario matches any of the routes in the routing configuration (focus on the description). +// 3. Find the route that best matches. +// 4. Use context clues from the entire conversation to determine the best fit. +// 5. Return the best match possible. You only response the name of the route that best matches the user's request, use the exact name in the routes config. +// 6. If no route relatively close to matches the user's latest intent or user last message is thank you or greeting, return an empty route ''. + +// # OUTPUT FORMAT +// Your final output must follow this JSON format: +// { +// "route": "route_name" # The matched route name, or empty string '' if no match +// } + +// Based on your analysis, provide only the JSON object as your final output with no additional text, explanations, or whitespace. +// "#; diff --git a/crates/llm_gateway/src/stream_context.rs b/crates/llm_gateway/src/stream_context.rs index 5b741a43..19fa735e 100644 --- a/crates/llm_gateway/src/stream_context.rs +++ b/crates/llm_gateway/src/stream_context.rs @@ -9,9 +9,10 @@ use common::consts::{ RATELIMIT_SELECTOR_HEADER_KEY, REQUEST_ID_HEADER, TRACE_PARENT_HEADER, }; use common::errors::ServerError; +use common::http::Client; use common::llm_providers::LlmProviders; use common::ratelimit::Header; -use common::stats::{IncrementingMetric, RecordingMetric}; +use common::stats::{Gauge, IncrementingMetric, RecordingMetric}; use common::tracing::{Event, Span, TraceData, Traceparent}; use common::{ratelimit, routing, tokenizer}; use http::StatusCode; @@ -19,12 +20,16 @@ use log::{debug, info, warn}; use proxy_wasm::hostcalls::get_current_time; use proxy_wasm::traits::*; use proxy_wasm::types::*; -use std::collections::VecDeque; +use std::cell::RefCell; +use std::collections::{HashMap, VecDeque}; use std::num::NonZero; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +#[derive(Debug)] +pub struct CallContext {} + pub struct StreamContext { context_id: u32, metrics: Rc, @@ -32,7 +37,7 @@ pub struct StreamContext { streaming_response: bool, response_tokens: usize, is_chat_completions_request: bool, - llm_providers: Rc, + pub(crate) llm_providers: Rc, llm_provider: Option>, request_id: Option, start_time: SystemTime, @@ -43,6 +48,10 @@ pub struct StreamContext { user_message: Option, traces_queue: Arc>>, overrides: Rc>, + pub(crate) request_body: Option, + pub(crate) request_size: Option, + pub(crate) chat_completion_request: Option, + callouts: RefCell>, } impl StreamContext { @@ -71,8 +80,13 @@ impl StreamContext { user_message: None, traces_queue, request_body_sent_time: None, + request_body: None, + request_size: None, + chat_completion_request: None, + callouts: RefCell::new(HashMap::new()), } } + fn llm_provider(&self) -> &LlmProvider { self.llm_provider .as_ref() @@ -156,7 +170,7 @@ impl StreamContext { }); } - fn send_server_error(&self, error: ServerError, override_status_code: Option) { + pub fn send_server_error(&self, error: ServerError, override_status_code: Option) { warn!("server error occurred: {}", error); self.send_http_response( override_status_code @@ -228,6 +242,7 @@ impl HttpContext for StreamContext { stream: None, port: None, rate_limits: None, + usage: None, })); } else { self.select_llm_provider(); @@ -321,7 +336,7 @@ impl HttpContext for StreamContext { // deserialized_body.metadata = None; // delete model key from message array for message in deserialized_body.messages.iter_mut() { - message.model = None; + // message.model = None; } self.user_message = deserialized_body @@ -331,43 +346,45 @@ impl HttpContext for StreamContext { .last() .cloned(); - let model_name = match self.llm_provider.as_ref() { - Some(llm_provider) => llm_provider.model.as_ref(), - None => None, - }; + // let model_name = match self.llm_provider.as_ref() { + // Some(llm_provider) => llm_provider.model.as_ref(), + // None => None, + // }; - let use_agent_orchestrator = match self.overrides.as_ref() { - Some(overrides) => overrides.use_agent_orchestrator.unwrap_or_default(), - None => false, - }; + // let use_agent_orchestrator = match self.overrides.as_ref() { + // Some(overrides) => overrides.use_agent_orchestrator.unwrap_or_default(), + // None => false, + // }; let model_requested = deserialized_body.model.clone(); - if deserialized_body.model.is_empty() || deserialized_body.model.to_lowercase() == "none" { - deserialized_body.model = match model_name { - Some(model_name) => model_name.clone(), - None => { - if use_agent_orchestrator { - "agent_orchestrator".to_string() - } else { - self.send_server_error( - ServerError::BadRequest { - why: format!("No model specified in request and couldn't determine model name from arch_config. Model name in req: {}, arch_config, provider: {}, model: {:?}", deserialized_body.model, self.llm_provider().name, self.llm_provider().model).to_string(), - }, - Some(StatusCode::BAD_REQUEST), - ); - return Action::Continue; - } - } - } - } + // if deserialized_body.model.is_empty() || deserialized_body.model.to_lowercase() == "none" { + // deserialized_body.model = match model_name { + // Some(model_name) => model_name.clone(), + // None => { + // if use_agent_orchestrator { + // "agent_orchestrator".to_string() + // } else { + // self.send_server_error( + // ServerError::BadRequest { + // why: format!("No model specified in request and couldn't determine model name from arch_config. Model name in req: {}, arch_config, provider: {}, model: {:?}", deserialized_body.model, self.llm_provider().name, self.llm_provider().model).to_string(), + // }, + // Some(StatusCode::BAD_REQUEST), + // ); + // return Action::Continue; + // } + // } + // } + // } info!( - "on_http_request_body: provider: {}, model requested: {}, model selected: {}", + "on_http_request_body: provider: {}, model requested: {}, model selected: {:?}", self.llm_provider().name, model_requested, - model_name.unwrap_or(&"None".to_string()), + self.llm_provider().model, ); + deserialized_body.model = self.llm_provider().model.clone().unwrap(); + let chat_completion_request_str = serde_json::to_string(&deserialized_body).unwrap(); debug!( @@ -404,7 +421,12 @@ impl HttpContext for StreamContext { self.set_http_request_body(0, body_size, chat_completion_request_str.as_bytes()); - Action::Continue + self.chat_completion_request = Some(deserialized_body); + self.request_body = Some(chat_completion_request_str); + self.request_size = Some(body_size); + + return Action::Continue; + // return self.route(); } fn on_http_response_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action { @@ -665,4 +687,50 @@ fn current_time_ns() -> u128 { .as_nanos() } -impl Context for StreamContext {} +impl Context for StreamContext { + fn on_http_call_response( + &mut self, + token_id: u32, + _num_headers: usize, + body_size: usize, + _num_trailers: usize, + ) { + debug!( + "on_http_call_response [S={}] token_id={} num_headers={} body_size={} num_trailers={}", + self.context_id, token_id, _num_headers, body_size, _num_trailers + ); + + let _callout_data = self + .callouts + .borrow_mut() + .remove(&token_id) + .expect("invalid token_id"); + + let body = self + .get_http_call_response_body(0, body_size) + .unwrap_or_default(); + + info!( + "on_http_call_response: response body: {}", + String::from_utf8_lossy(&body) + ); + + self.set_http_request_body( + 0, + self.request_size.unwrap(), + self.request_body.as_ref().unwrap().as_bytes(), + ); + } +} + +impl Client for StreamContext { + type CallContext = CallContext; + + fn callouts(&self) -> &RefCell> { + &self.callouts + } + + fn active_http_calls(&self) -> &Gauge { + &self.metrics.active_http_calls + } +} diff --git a/crates/prompt_gateway/src/stream_context.rs b/crates/prompt_gateway/src/stream_context.rs index 1cd2fa86..7345586a 100644 --- a/crates/prompt_gateway/src/stream_context.rs +++ b/crates/prompt_gateway/src/stream_context.rs @@ -103,7 +103,7 @@ impl StreamContext { } } - pub fn send_server_error(&self, error: ServerError, override_status_code: Option) { + pub (crate) fn send_server_error(&self, error: ServerError, override_status_code: Option) { self.send_http_response( override_status_code .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR) diff --git a/crates/whitestaff/Cargo.toml b/crates/whitestaff/Cargo.toml new file mode 100644 index 00000000..ca9b0ca0 --- /dev/null +++ b/crates/whitestaff/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "whitestaff" +version = "0.1.0" +edition = "2021" + +[dependencies] +bytes = "1.10.1" +common = { version = "0.1.0", path = "../common" } +eventsource-client = "0.15.0" +eventsource-stream = "0.2.3" +futures = "0.3.31" +http-body-util = "0.1.3" +hyper = { version="1.6.0", features = ["full"] } +hyper-util = "0.1.11" +opentelemetry = "0.29.1" +opentelemetry-http = "0.29.0" +opentelemetry-otlp = "0.29.0" +opentelemetry-stdout = "0.29.0" +opentelemetry_sdk = "0.29.0" +reqwest = "0.12.15" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +serde_yaml = "0.9.34" +thiserror = "2.0.12" +tokio = { version = "1.44.2", features = ["full"] } +tokio-stream = "0.1.17" +tracing = "0.1.41" +tracing-opentelemetry = "0.30.0" +tracing-subscriber = { version="0.3.19", features = ["env-filter", "fmt"] } diff --git a/crates/whitestaff/src/consts2.rs b/crates/whitestaff/src/consts2.rs new file mode 100644 index 00000000..51764b71 --- /dev/null +++ b/crates/whitestaff/src/consts2.rs @@ -0,0 +1,32 @@ +pub const SYSTEM_PROMPT_Z: &str = r#" +You are an advanced Routing Assistant designed to select the optimal route based on user requests. +Your task is to analyze conversations and match them to the most appropriate predefined route. +Review the available routes config: + +# ROUTES CONFIG START +{routes} +# ROUTES CONFIG END + +Examine the following conversation between a user and an assistant: + +# CONVERSATION START +{conversation} +# CONVERSATION END + +Your goal is to identify the most appropriate route that matches the user's LATEST intent. Follow these steps: + +1. Carefully read and analyze the provided conversation, focusing on the user's latest request and the conversation scenario. +2. Check if the user's request and scenario matches any of the routes in the routing configuration (focus on the description). +3. Find the route that best matches. +4. Use context clues from the entire conversation to determine the best fit. +5. Return the best match possible. You only response the name of the route that best matches the user's request, use the exact name in the routes config. +6. If no route relatively close to matches the user's latest intent or user last message is thank you or greeting, return an empty route ''. + +# OUTPUT FORMAT +Your final output must follow this JSON format: +{ + "route": "route_name" # The matched route name, or empty string '' if no match +} + +Based on your analysis, provide only the JSON object as your final output with no additional text, explanations, or whitespace. +"#; diff --git a/crates/whitestaff/src/lib.rs b/crates/whitestaff/src/lib.rs new file mode 100644 index 00000000..dfef3bc2 --- /dev/null +++ b/crates/whitestaff/src/lib.rs @@ -0,0 +1,3 @@ +mod consts2; +mod router; +mod types; diff --git a/crates/whitestaff/src/main.rs b/crates/whitestaff/src/main.rs new file mode 100644 index 00000000..171b8e8e --- /dev/null +++ b/crates/whitestaff/src/main.rs @@ -0,0 +1,406 @@ +use bytes::Bytes; +use common::api::open_ai::{ChatCompletionsRequest, ChatCompletionsResponse, Message}; +use common::configuration::{Configuration, LlmProvider}; +use common::consts::{ARCH_PROVIDER_HINT_HEADER, USER_ROLE}; +use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full}; +use hyper::body::{Body, Incoming}; +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper::{header, Method, Request, Response, StatusCode}; +use hyper_util::rt::TokioIo; +use opentelemetry::global::BoxedTracer; +use opentelemetry::trace::FutureExt; +use opentelemetry::{ + global, + trace::{SpanKind, Tracer}, + Context, +}; +use opentelemetry_http::HeaderExtractor; +use opentelemetry_sdk::{propagation::TraceContextPropagator, trace::SdkTracerProvider}; +use opentelemetry_stdout::SpanExporter; +use types::types::LlmRouterResponse; +use std::env; +use std::sync::{Arc, OnceLock}; +use tokio::net::TcpListener; +use tracing::info; +use tracing_subscriber::EnvFilter; + +mod consts2; +use consts2::SYSTEM_PROMPT_Z; +mod types; + +const BIND_ADDRESS: &str = "0.0.0.0:9091"; + +fn get_tracer() -> &'static BoxedTracer { + static TRACER: OnceLock = OnceLock::new(); + TRACER.get_or_init(|| global::tracer("archgw/whitestaff")) +} + +// Utility function to extract the context from the incoming request headers +fn extract_context_from_request(req: &Request) -> Context { + global::get_text_map_propagator(|propagator| { + propagator.extract(&HeaderExtractor(req.headers())) + }) +} + +fn init_tracer() -> SdkTracerProvider { + global::set_text_map_propagator(TraceContextPropagator::new()); + // Install stdout exporter pipeline to be able to retrieve the collected spans. + // For the demonstration, use `Sampler::AlwaysOn` sampler to sample all traces. + let provider = SdkTracerProvider::builder() + .with_simple_exporter(SpanExporter::default()) + .build(); + + global::set_tracer_provider(provider.clone()); + provider +} + +fn empty() -> BoxBody { + Empty::::new() + .map_err(|never| match never {}) + .boxed() +} + +fn full>(chunk: T) -> BoxBody { + Full::new(chunk.into()) + .map_err(|never| match never {}) + .boxed() +} + +fn shorten_string(s: &str) -> String { + if s.len() > 80 { + format!("{}...", &s[..80]) + } else { + s.to_string() + } +} + +async fn chat_completion( + req: Request, + arch_config: Arc, +) -> Result>, hyper::Error> { + let max = req.body().size_hint().upper().unwrap_or(u64::MAX); + if max > 1024 * 1024 { + let error_msg = format!("Request body too large: {} bytes", max); + let mut too_large = Response::new(full(error_msg)); + *too_large.status_mut() = StatusCode::PAYLOAD_TOO_LARGE; + return Ok(too_large); + } + + let mut request_headers = req.headers().clone(); + + info!( + "Request headers: {}", + request_headers + .iter() + .map(|(k, v)| format!("{}: {}", k, v.to_str().unwrap_or_default())) + .collect::>() + .join(", ") + ); + let chat_request_bytes = req.collect().await?.to_bytes(); + let chat_completion_request: ChatCompletionsRequest = + match serde_json::from_slice(&chat_request_bytes) { + Ok(request) => request, + Err(err) => { + let err_msg = format!("Failed to parse request body: {}", err); + let mut bad_request = Response::new(full(err_msg)); + *bad_request.status_mut() = StatusCode::BAD_REQUEST; + return Ok(bad_request); + } + }; + + info!( + "Received request: {}", + &serde_json::to_string(&chat_completion_request).unwrap() + ); + + let llm_providers: Vec = chat_completion_request + .metadata + .as_ref() + .and_then(|metadata| metadata.get("llm_providers")) + .and_then(|providers| serde_json::from_str::>(providers).ok()) + .unwrap_or_default(); + + info!( + "llm_providers from request: {}...", + shorten_string(&serde_json::to_string(&llm_providers).unwrap()) + ); + + let llm_router_with_usage = arch_config + .llm_providers + .iter() + .filter(|provider| provider.usage.is_some()).cloned() + .collect::>(); + + // convert the llm_providers to yaml string but only include name and usage + let llm_providers_yaml = llm_router_with_usage + .iter() + .map(|provider| { + format!( + "- name: {}()\n description: {}", + provider.name, + provider.usage.as_ref().unwrap_or(&"".to_string()) + ) + }) + .collect::>() + .join("\n"); + + info!( + "llm_providers from config: {}...", + shorten_string(&llm_providers_yaml.replace("\n", "\\n")) + ); + + let message = SYSTEM_PROMPT_Z + .replace("{routes}", &llm_providers_yaml) + .replace( + "{conversation}", + &serde_json::to_string_pretty(&chat_completion_request.messages).unwrap(), + ); + + let router_request: ChatCompletionsRequest = ChatCompletionsRequest { + model: "cotran2/llama-1b-4-26".to_string(), + messages: vec![Message { + content: Some(message), + role: USER_ROLE.to_string(), + model: None, + tool_calls: None, + tool_call_id: None, + }], + tools: None, + stream: false, + stream_options: None, + metadata: None, + }; + + info!( + "router_request: {}...", + shorten_string(&serde_json::to_string(&router_request).unwrap()) + ); + + let trace_parent = request_headers + .iter() + .find(|(ty, _)| ty.as_str() == "traceparent") + .map(|(_, value)| value.to_str().unwrap_or_default()); + + let mut llm_route_request_headers = header::HeaderMap::new(); + llm_route_request_headers.insert( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ); + + // attach traceparent header to the llm router request + if let Some(trace_parent) = trace_parent { + llm_route_request_headers.insert( + header::HeaderName::from_static("traceparent"), + header::HeaderValue::from_str(trace_parent).unwrap(), + ); + } + + llm_route_request_headers.insert( + header::HeaderName::from_static("host"), + header::HeaderValue::from_static("router_model_host"), + ); + + let res = match reqwest::Client::new() + .post("http://localhost:9090/v1/chat/completions") + .headers(llm_route_request_headers) + .body(serde_json::to_string(&router_request).unwrap()) + .send() + .await + { + Ok(res) => res, + Err(err) => { + let err_msg = format!("Failed to send request: {}", err); + let mut internal_error = Response::new(full(err_msg)); + *internal_error.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + return Ok(internal_error); + } + }; + + let body = match res.text().await { + Ok(body) => body, + Err(err) => { + let err_msg = format!("Failed to read response: {}", err); + let mut internal_error = Response::new(full(err_msg)); + *internal_error.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + return Ok(internal_error); + } + }; + + let chat_completion_response: ChatCompletionsResponse = match serde_json::from_str(&body) { + Ok(response) => response, + Err(err) => { + let err_msg = format!("Failed to parse response: {}", err); + let mut internal_error = Response::new(full(err_msg)); + *internal_error.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + return Ok(internal_error); + } + }; + + info!( + "chat_completion_response: {}", + shorten_string(&serde_json::to_string(&chat_completion_response).unwrap()) + ); + + let router_resp = chat_completion_response.choices[0] + .message + .content + .as_ref() + .unwrap(); + let router_resp_fixed = router_resp.replace("'", "\""); + let router_response: LlmRouterResponse = match serde_json::from_str(router_resp_fixed.as_str()) + { + Ok(response) => response, + Err(err) => { + let err_msg = format!("Failed to parse response: {}", err); + let mut internal_error = Response::new(full(err_msg)); + *internal_error.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + return Ok(internal_error); + } + }; + + info!( + "router_response json: {}", + serde_json::to_string(&router_response).unwrap() + ); + + let selecter_llm = router_response + .route + .map(|route| route.strip_suffix("()").unwrap_or_default().to_string()) + .unwrap_or_default(); + + if selecter_llm.is_empty() { + let conversation = &serde_json::to_string(&chat_completion_request.messages).unwrap(); + info!( + "no route selected for conversation: {}", + shorten_string(conversation) + ); + } + + info!("selecter_llm: {}", selecter_llm); + + if let Some(trace_parent) = trace_parent { + request_headers.insert( + header::HeaderName::from_static("traceparent"), + header::HeaderValue::from_str(trace_parent).unwrap(), + ); + } + + if !selecter_llm.is_empty() { + request_headers.insert( + ARCH_PROVIDER_HINT_HEADER, + header::HeaderValue::from_str(&selecter_llm).unwrap(), + ); + } + + let llm_response = match reqwest::Client::new() + .post("http://localhost:12000/v1/chat/completions") + .headers(request_headers) + .body(chat_request_bytes) + .send() + .await + { + Ok(res) => res, + Err(err) => { + let err_msg = format!("Failed to send request: {}", err); + let mut internal_error = Response::new(full(err_msg)); + *internal_error.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + return Ok(internal_error); + } + }; + + let body = match llm_response.text().await { + Ok(body) => body, + Err(err) => { + let err_msg = format!("Failed to read response: {}", err); + let mut internal_error = Response::new(full(err_msg)); + *internal_error.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + return Ok(internal_error); + } + }; + + let mut ok_response = Response::new(full(body)); + *ok_response.status_mut() = StatusCode::OK; + + Ok(ok_response) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let _tracer_provider = init_tracer(); + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), + ) + .init(); + + let bind_address = env::var("BIND_ADDRESS").unwrap_or_else(|_| BIND_ADDRESS.to_string()); + + //loading arch_config.yaml file + let arch_config_path = + env::var("ARCH_CONFIG_PATH").unwrap_or_else(|_| "arch_config.yaml".to_string()); + info!("Loading arch_config.yaml from {}", arch_config_path); + let arch_config = + std::fs::read_to_string(&arch_config_path).expect("Failed to read arch_config.yaml"); + let config: Configuration = + serde_yaml::from_str(&arch_config).expect("Failed to parse arch_config.yaml"); + let arch_config = Arc::new(config); + info!( + "arch_config: {:?}", + shorten_string(&serde_json::to_string(arch_config.as_ref()).unwrap()) + ); + + info!("Listening on http://{}", bind_address); + let listener = TcpListener::bind(bind_address).await?; + + loop { + let (stream, _) = listener.accept().await?; + let peer_addr = stream.peer_addr()?; + let io = TokioIo::new(stream); + + let arch_config = Arc::clone(&arch_config); + + let service = service_fn(move |req| { + let arch_config = Arc::clone(&arch_config); + let parent_cx = extract_context_from_request(&req); + info!("parent_cx: {:?}", parent_cx); + let tracer = get_tracer(); + let _span = tracer + .span_builder("chat_completion") + .with_kind(SpanKind::Server) + .start_with_context(tracer, &parent_cx); + + async move { + match (req.method(), req.uri().path()) { + (&Method::POST, "/v1/chat/completions") => { + info!( + "config: {:?}", + shorten_string( + &serde_json::to_string(&arch_config.llm_providers).unwrap() + ) + ); + chat_completion(req, arch_config) + .with_context(parent_cx) + .await + } + _ => { + let mut not_found = Response::new(empty()); + *not_found.status_mut() = StatusCode::NOT_FOUND; + Ok(not_found) + } + } + } + }); + + tokio::task::spawn(async move { + info!("Accepted connection from {:?}", peer_addr); + if let Err(err) = http1::Builder::new() + // .serve_connection(io, service_fn(chat_completion)) + .serve_connection(io, service) + .await + { + info!("Error serving connection: {:?}", err); + } + }); + } +} diff --git a/crates/whitestaff/src/router/consts.rs b/crates/whitestaff/src/router/consts.rs new file mode 100644 index 00000000..1128bf32 --- /dev/null +++ b/crates/whitestaff/src/router/consts.rs @@ -0,0 +1,32 @@ +pub const ARCH_ROUTER_V1_SYSTEM_PROMPT: &str = r#" +You are an advanced Routing Assistant designed to select the optimal route based on user requests. +Your task is to analyze conversations and match them to the most appropriate predefined route. +Review the available routes config: + +# ROUTES CONFIG START +{routes} +# ROUTES CONFIG END + +Examine the following conversation between a user and an assistant: + +# CONVERSATION START +{conversation} +# CONVERSATION END + +Your goal is to identify the most appropriate route that matches the user's LATEST intent. Follow these steps: + +1. Carefully read and analyze the provided conversation, focusing on the user's latest request and the conversation scenario. +2. Check if the user's request and scenario matches any of the routes in the routing configuration (focus on the description). +3. Find the route that best matches. +4. Use context clues from the entire conversation to determine the best fit. +5. Return the best match possible. You only response the name of the route that best matches the user's request, use the exact name in the routes config. +6. If no route relatively close to matches the user's latest intent or user last message is thank you or greeting, return an empty route ''. + +# OUTPUT FORMAT +Your final output must follow this JSON format: +{ + "route": "route_name" # The matched route name, or empty string '' if no match +} + +Based on your analysis, provide only the JSON object as your final output with no additional text, explanations, or whitespace. +"#; diff --git a/crates/whitestaff/src/router/llm_router.rs b/crates/whitestaff/src/router/llm_router.rs new file mode 100644 index 00000000..beb6f85e --- /dev/null +++ b/crates/whitestaff/src/router/llm_router.rs @@ -0,0 +1,164 @@ +use common::{ + api::open_ai::{ChatCompletionsRequest, ChatCompletionsResponse, Message}, + configuration::LlmProvider, + consts::USER_ROLE, +}; +use hyper::header; +use thiserror::Error; +use tracing::info; + +use crate::{router::consts::ARCH_ROUTER_V1_SYSTEM_PROMPT, types::types::LlmRouterResponse}; + +// Domain Service example +pub struct RouterService { + providers: Vec, + providers_with_usage: Vec, + router_url: String, + client: reqwest::Client, + llm_providers_with_usage_yaml: String, +} + +#[derive(Debug, Error)] +pub enum RoutingError { + #[error("Failed to send request: {0}")] + RequestError(#[from] reqwest::Error), + #[error("Failed to parse JSON: {0}")] + JsonError(#[from] serde_json::Error), +} + +type Result = std::result::Result; + +impl RouterService { + pub fn new(providers: Vec, router_url: String) -> Self { + let providers_with_usage = providers + .iter() + .filter(|provider| provider.usage.is_some()) + .cloned() + .collect::>(); + + // convert the llm_providers to yaml string but only include name and usage + let llm_providers_with_usage_yaml = providers_with_usage + .iter() + .map(|provider| { + format!( + "- name: {}()\n description: {}", + provider.name, + provider.usage.as_ref().unwrap_or(&"".to_string()) + ) + }) + .collect::>() + .join("\n"); + + info!( + "llm_providers from config with usage: {}...", + &llm_providers_with_usage_yaml.replace("\n", "\\n") + ); + + RouterService { + providers, + providers_with_usage, + router_url, + llm_providers_with_usage_yaml, + client: reqwest::Client::new(), + } + } + + pub async fn determine_route( + &self, + chat_completion_request: &ChatCompletionsRequest, + ) -> Result { + let message = ARCH_ROUTER_V1_SYSTEM_PROMPT + .replace("{routes}", &self.llm_providers_with_usage_yaml) + .replace( + "{conversation}", + &serde_json::to_string_pretty(&chat_completion_request.messages).unwrap(), + ); + + let router_request: ChatCompletionsRequest = ChatCompletionsRequest { + model: "cotran2/llama-1b-4-26".to_string(), + messages: vec![Message { + content: Some(message), + role: USER_ROLE.to_string(), + model: None, + tool_calls: None, + tool_call_id: None, + }], + tools: None, + stream: false, + stream_options: None, + metadata: None, + }; + + info!( + "router_request: {}", + &serde_json::to_string(&router_request).unwrap() + ); + + // let trace_parent = request_headers + // .iter() + // .find(|(ty, _)| ty.as_str() == "traceparent") + // .map(|(_, value)| value.to_str().unwrap_or_default()); + + let mut llm_route_request_headers = header::HeaderMap::new(); + llm_route_request_headers.insert( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ); + + // // attach traceparent header to the llm router request + // if let Some(trace_parent) = trace_parent { + // llm_route_request_headers.insert( + // header::HeaderName::from_static("traceparent"), + // header::HeaderValue::from_str(trace_parent).unwrap(), + // ); + // } + + llm_route_request_headers.insert( + header::HeaderName::from_static("host"), + header::HeaderValue::from_static("router_model_host"), + ); + + let res = reqwest::Client::new() + .post(&self.router_url) + .headers(llm_route_request_headers) + .body(serde_json::to_string(&router_request).unwrap()) + .send() + .await?; + + let body = res.text().await?; + + let chat_completion_response: ChatCompletionsResponse = serde_json::from_str(&body)?; + + info!( + "chat_completion_response: {}", + &serde_json::to_string(&chat_completion_response).unwrap() + ); + + let router_resp = chat_completion_response.choices[0] + .message + .content + .as_ref() + .unwrap(); + let router_resp_fixed = router_resp.replace("'", "\""); + let router_response: LlmRouterResponse = serde_json::from_str(router_resp_fixed.as_str())?; + + info!( + "router_response json: {}", + serde_json::to_string(&router_response).unwrap() + ); + + let selecter_llm = router_response + .route + .map(|route| route.strip_suffix("()").unwrap_or_default().to_string()) + .unwrap_or_default(); + + if selecter_llm.is_empty() { + let conversation = &serde_json::to_string(&chat_completion_request.messages).unwrap(); + info!("no route selected for conversation: {}", conversation); + } + + info!("selecter_llm: {}", selecter_llm); + + Ok(self.router_url.clone()) + } +} diff --git a/crates/whitestaff/src/router/mod.rs b/crates/whitestaff/src/router/mod.rs new file mode 100644 index 00000000..c9757892 --- /dev/null +++ b/crates/whitestaff/src/router/mod.rs @@ -0,0 +1,2 @@ +pub mod llm_router; +mod consts; diff --git a/crates/whitestaff/src/types/mod.rs b/crates/whitestaff/src/types/mod.rs new file mode 100644 index 00000000..cd408564 --- /dev/null +++ b/crates/whitestaff/src/types/mod.rs @@ -0,0 +1 @@ +pub mod types; diff --git a/crates/whitestaff/src/types/types.rs b/crates/whitestaff/src/types/types.rs new file mode 100644 index 00000000..0e929413 --- /dev/null +++ b/crates/whitestaff/src/types/types.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LlmRouterResponse { + pub route: Option, +} diff --git a/demos/use_cases/preference_based_routing/README.md b/demos/use_cases/preference_based_routing/README.md new file mode 100644 index 00000000..08c06dc4 --- /dev/null +++ b/demos/use_cases/preference_based_routing/README.md @@ -0,0 +1,58 @@ +# LLM Routing +This demo shows how you can arch gateway to manage keys and route to upstream LLM. + +# Starting the demo +1. Please make sure the [pre-requisites](https://github.com/katanemo/arch/?tab=readme-ov-file#prerequisites) are installed correctly +1. Start Arch + ```sh + sh run_demo.sh + ``` +1. Navigate to http://localhost:18080/ + +Following screen shows an example of interaction with arch gateway showing dynamic routing. You can select between different LLMs using "override model" option in the chat UI. + +![LLM Routing Demo](llm_routing_demo.png) + +You can also pass in a header to override model when sending prompt. Following example shows how you can use `x-arch-llm-provider-hint` header to override model selection, + +```bash + +$ curl --header 'Content-Type: application/json' \ + --header 'x-arch-llm-provider-hint: ministral-3b' \ + --data '{"messages": [{"role": "user","content": "hello"}]}' \ + http://localhost:12000/v1/chat/completions 2> /dev/null | jq . +{ + "id": "xxx", + "object": "chat.completion", + "created": 1737760394, + "model": "ministral-3b-latest", + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "tool_calls": null, + "content": "Hello! How can I assist you today? Let's chat about anything you'd like. 😊" + }, + "finish_reason": "stop" + } + ], + "usage": { + "prompt_tokens": 4, + "total_tokens": 25, + "completion_tokens": 21 + } +} + +``` + +# Observability +Arch gateway publishes stats endpoint at http://localhost:19901/stats. In this demo we are using prometheus to pull stats from arch and we are using grafana to visualize the stats in dashboard. To see grafana dashboard follow instructions below, + +1. Navigate to http://localhost:3000/ to open grafana UI (use admin/grafana as credentials) +1. From grafana left nav click on dashboards and select "Intelligent Gateway Overview" to view arch gateway stats +1. For tracing you can head over to http://localhost:16686/ to view recent traces. + +Following is a screenshot of tracing UI showing call received by arch gateway and making upstream call to LLM, + +![Jaeger Tracing](jaeger_tracing_llm_routing.png) diff --git a/demos/use_cases/preference_based_routing/arch_config.yaml b/demos/use_cases/preference_based_routing/arch_config.yaml new file mode 100644 index 00000000..9ca5a35a --- /dev/null +++ b/demos/use_cases/preference_based_routing/arch_config.yaml @@ -0,0 +1,33 @@ +version: "0.1-beta" + +endpoints: + gcp_hosted_outer_llm: + endpoint: 34.46.85.85:8000 + http_host: 34.46.85.85 + # endpoint: host.docker.internal:11223 + +listeners: + egress_traffic: + address: 0.0.0.0 + port: 12000 + message_format: openai + timeout: 30s + +llm_providers: + + - name: gpt-4o + provider_interface: openai + access_key: $OPENAI_API_KEY + model: gpt-4o + usage: | + complex reasoning problem, require multi step answer + + - name: o4-mini + provider_interface: openai + access_key: $OPENAI_API_KEY + model: o4-mini + usage: | + simple requests, basic fact retrieval, easy to answer + +tracing: + random_sampling: 100 diff --git a/demos/use_cases/preference_based_routing/convert_system_prompt.py b/demos/use_cases/preference_based_routing/convert_system_prompt.py new file mode 100644 index 00000000..5178cf41 --- /dev/null +++ b/demos/use_cases/preference_based_routing/convert_system_prompt.py @@ -0,0 +1,62 @@ +import json +import yaml + +system_prompt = """ +You are an advanced Routing Assistant designed to select the optimal route based on user requests. +Your task is to analyze conversations and match them to the most appropriate predefined route. +Review the available routes config: + +# ROUTES CONFIG START +{routes} +# ROUTES CONFIG END + +Examine the following conversation between a user and an assistant: + +# CONVERSATION START +{conversation} +# CONVERSATION END + +Your goal is to identify the most appropriate route that matches the user's LATEST intent. Follow these steps: + +1. Carefully read and analyze the provided conversation, focusing on the user's latest request and the conversation scenario. +2. Check if the user's request and scenario matches any of the routes in the routing configuration (focus on the description). +3. Find the route that best matches. +4. Use context clues from the entire conversation to determine the best fit. +5. Return the best match possible. You only response the name of the route that best matches the user's request, use the exact name in the routes config. +6. If no route relatively close to matches the user's latest intent or user last message is thank you or greeting, return an empty route ''. +""" + +output_format = """ +# OUTPUT FORMAT +Your final output must follow this JSON format: +{ + "route": "route_name" # The matched route name, or empty string '' if no match +} + +Based on your analysis, provide only the JSON object as your final output with no additional text, explanations, or whitespace. +""" + + +with open("arch_config.yaml", "r") as file: + data = yaml.safe_load(file) + +llm_provider_routes = "" + +for llm_provider in data.get("llm_providers", []): + llm_provider_routes += f"- name: {llm_provider.get('name')}()\n" + llm_provider_routes += f" description: {json.dumps(llm_provider.get('usage'))}\n" + + +conversation = """ +user: Hello +assistant: Hi! How can I assist you today? +user: I want to know how far is sun from earth. +""" + +system_prompt_formatted = system_prompt.format( + routes=llm_provider_routes, conversation=conversation +) + +system_prompt_2 = f"{system_prompt_formatted}\n{output_format}" +print(system_prompt_2) +print(json.dumps(system_prompt_2, indent=2)) diff --git a/demos/use_cases/preference_based_routing/docker-compose.yaml b/demos/use_cases/preference_based_routing/docker-compose.yaml new file mode 100644 index 00000000..c2d794c6 --- /dev/null +++ b/demos/use_cases/preference_based_routing/docker-compose.yaml @@ -0,0 +1,32 @@ +services: + + chatbot_ui: + build: + context: ../../shared/chatbot_ui + dockerfile: Dockerfile + ports: + - "18080:8080" + environment: + - CHAT_COMPLETION_ENDPOINT=http://host.docker.internal:12000/v1 + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./arch_config.yaml:/app/arch_config.yaml + + jaeger: + build: + context: ../../shared/jaeger + ports: + - "16686:16686" + - "4317:4317" + - "4318:4318" + + prometheus: + build: + context: ../../shared/prometheus + + grafana: + build: + context: ../../shared/grafana + ports: + - "3000:3000" diff --git a/demos/use_cases/preference_based_routing/jaeger_tracing_llm_routing.png b/demos/use_cases/preference_based_routing/jaeger_tracing_llm_routing.png new file mode 100644 index 00000000..e18016d1 Binary files /dev/null and b/demos/use_cases/preference_based_routing/jaeger_tracing_llm_routing.png differ diff --git a/demos/use_cases/preference_based_routing/llm_routing_demo.png b/demos/use_cases/preference_based_routing/llm_routing_demo.png new file mode 100644 index 00000000..50f25677 Binary files /dev/null and b/demos/use_cases/preference_based_routing/llm_routing_demo.png differ diff --git a/demos/use_cases/preference_based_routing/run_demo.sh b/demos/use_cases/preference_based_routing/run_demo.sh new file mode 100644 index 00000000..c0eafee6 --- /dev/null +++ b/demos/use_cases/preference_based_routing/run_demo.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -e + +# Function to start the demo +start_demo() { + # Step 1: Check if .env file exists + if [ -f ".env" ]; then + echo ".env file already exists. Skipping creation." + else + # Step 2: Create `.env` file and set OpenAI key + if [ -z "$OPENAI_API_KEY" ]; then + echo "Error: OPENAI_API_KEY environment variable is not set for the demo." + exit 1 + fi + + echo "Creating .env file..." + echo "OPENAI_API_KEY=$OPENAI_API_KEY" > .env + echo ".env file created with OPENAI_API_KEY." + fi + + # Step 3: Start Arch + echo "Starting Arch with arch_config.yaml..." + archgw up arch_config.yaml + + # Step 4: Start LLM Routing + echo "Starting LLM Routing using Docker Compose..." + docker compose up -d # Run in detached mode +} + +# Function to stop the demo +stop_demo() { + # Step 1: Stop Docker Compose services + echo "Stopping LLM Routing using Docker Compose..." + docker compose down + + # Step 2: Stop Arch + echo "Stopping Arch..." + archgw down +} + +# Main script logic +if [ "$1" == "down" ]; then + stop_demo +else + # Default action is to bring the demo up + start_demo +fi diff --git a/demos/use_cases/preference_based_routing/staff_req.json b/demos/use_cases/preference_based_routing/staff_req.json new file mode 100644 index 00000000..47195643 --- /dev/null +++ b/demos/use_cases/preference_based_routing/staff_req.json @@ -0,0 +1,12 @@ +{ + "model": "cotran2/llama-1b-4-26", + "messages": [ + { + "role": "user", + "content": "What is the capital of France?" + } + ], + "metadata": { + "llm_providers": "[]" + } +} diff --git a/demos/use_cases/preference_based_routing/test.rest b/demos/use_cases/preference_based_routing/test.rest new file mode 100644 index 00000000..f7c18cfb --- /dev/null +++ b/demos/use_cases/preference_based_routing/test.rest @@ -0,0 +1,24 @@ +@arch_llm_router_endpoint = http://34.30.16.38:8000 + +POST {{arch_llm_router_endpoint}}/v1/chat/completions HTTP/1.1 +Content-Type: application/json + +{ + "model": "cotran2/llama-1b-4-26", + "messages": [ + { + "role": "user", + "content": "You are an advanced Routing Assistant designed to select the optimal route based on user requests. \nYour task is to analyze conversations and match them to the most appropriate predefined route.\nReview the available routes config:\n\n# ROUTES CONFIG START\n- name: gpt-4o()\n description: \"complex reasoning problem, require multi step answer\\n\"\n- name: o4-mini()\n description: \"simple requests, basic fact retrieval, easy to answer\\n\"\n\n# ROUTES CONFIG END\n\nExamine the following conversation between a user and an assistant:\n\n# CONVERSATION START\n\nuser: Hello\nassistant: Hi! How can I assist you today?\nuser: List us presidents who are born in odd years and are still alive. Order them by their age and I also know what is their home city they were born. And what year they became president. Also give me summary of which president was the best for economy of the US.\n\n# CONVERSATION END\n\nYour goal is to identify the most appropriate route that matches the user's LATEST intent. Follow these steps:\n\n1. Carefully read and analyze the provided conversation, focusing on the user's latest request and the conversation scenario.\n2. Check if the user's request and scenario matches any of the routes in the routing configuration (focus on the description).\n3. Find the route that best matches.\n4. Use context clues from the entire conversation to determine the best fit.\n5. Return the best match possible. You only response the name of the route that best matches the user's request, use the exact name in the routes config.\n6. If no route relatively close to matches the user's latest intent or user last message is thank you or greeting, return an empty route ''. \n\n\n# OUTPUT FORMAT\nYour final output must follow this JSON format:\n{\n \"route\": \"route_name\" # The matched route name, or empty string '' if no match\n}\n\nBased on your analysis, provide only the JSON object as your final output with no additional text, explanations, or whitespace." + } + ] +} + +### test 2 + +POST {{arch_llm_router_endpoint}}/v1/chat/completions HTTP/1.1 +Content-Type: application/json + +{"model":"cotran2/llama-1b-4-26","messages":[{"role":"user","content":"\nYou are an advanced Routing Assistant designed to select the optimal route based on user requests. \nYour task is to analyze conversations and match them to the most appropriate predefined route.\nReview the available routes config:\n\n# ROUTES CONFIG START\n- name: gpt-4o\n description: simple requests, basic fact retrieval, easy to answer\n- name: o4-mini()\n description: complex reasoning problem, require multi step answer\n# ROUTES CONFIG END\n\nExamine the following conversation between a user and an assistant:\n\n# CONVERSATION START\n[{\"role\":\"user\",\"content\":\"What is the capital of France?\"}]\n# CONVERSATION END\n\nYour goal is to identify the most appropriate route that matches the user's LATEST intent. Follow these steps:\n\n1. Carefully read and analyze the provided conversation, focusing on the user's latest request and the conversation scenario.\n2. Check if the user's request and scenario matches any of the routes in the routing configuration (focus on the description).\n3. Find the route that best matches.\n4. Use context clues from the entire conversation to determine the best fit.\n5. Return the best match possible. You only response the name of the route that best matches the user's request, use the exact name in the routes config.\n6. If no route relatively close to matches the user's latest intent or user last message is thank you or greeting, return an empty route ''. \n\n# OUTPUT FORMAT\nYour final output must follow this JSON format:\n{\n \"route\": \"route_name\" # The matched route name, or empty string '' if no match\n}\n\nBased on your analysis, provide only the JSON object as your final output with no additional text, explanations, or whitespace.\n"}],"stream":false} + +### get model list +GET http://34.46.85.85:8000/v1/models HTTP/1.1 diff --git a/demos/use_cases/preference_based_routing/test_sytem_prompt.text b/demos/use_cases/preference_based_routing/test_sytem_prompt.text new file mode 100644 index 00000000..ea5efb6e --- /dev/null +++ b/demos/use_cases/preference_based_routing/test_sytem_prompt.text @@ -0,0 +1,31 @@ +You are an advanced Routing Assistant designed to select the optimal route based on user requests. +Your task is to analyze conversations and match them to the most appropriate predefined route. +Review the available routes config: + +# ROUTES CONFIG START +{} +# ROUTES CONFIG END + +Examine the following conversation between a user and an assistant: + +# CONVERSATION START +{} +# CONVERSATION END + +Your goal is to identify the most appropriate route that matches the user's LATEST intent. Follow these steps: + +1. Carefully read and analyze the provided conversation, focusing on the user's latest request and the conversation scenario. +2. Check if the user's request and scenario matches any of the routes in the routing configuration (focus on the description). +3. Find the route that best matches. +4. Use context clues from the entire conversation to determine the best fit. +5. Return the best match possible. You only response the name of the route that best matches the user's request, use the exact name in the routes config. +6. If no route relatively close to matches the user's latest intent or user last message is thank you or greeting, return an empty route ''. +""" +output_prompt = """ +# OUTPUT FORMAT +Your final output must follow this JSON format: +{ + "route": "route_name" # The matched route name, or empty string '' if no match +} + +Based on your analysis, provide only the JSON object as your final output with no additional text, explanations, or whitespace. diff --git a/tests/rest/llm_routing.rest b/tests/rest/llm_routing.rest new file mode 100644 index 00000000..41fcffca --- /dev/null +++ b/tests/rest/llm_routing.rest @@ -0,0 +1,77 @@ +@llm_endpoint = http://localhost:12000 +@openai_endpoint = https://api.openai.com +@access_key = {{$dotenv OPENAI_API_KEY}} + +### openai request +POST {{openai_endpoint}}/v1/chat/completions HTTP/1.1 +Content-Type: application/json +Authorization: Bearer {{access_key}} + +{ + "messages": [ + { + "role": "user", + "content": "hello" + } + ], + "model": "gpt-4o-mini", + "stream": true +} + +### openai request (streaming) +POST {{openai_endpoint}}/v1/chat/completions HTTP/1.1 +Content-Type: application/json +Authorization: Bearer {{access_key}} + +{ + "messages": [ + { + "role": "user", + "content": "hello" + } + ], + "model": "gpt-4o-mini", + "stream": true +} + + +### llm gateway request +POST {{llm_endpoint}}/v1/chat/completions HTTP/1.1 +Content-Type: application/json + +{ + "messages": [ + { + "role": "user", + "content": "hello" + } + ] +} + +### llm gateway request (streaming) +POST {{llm_endpoint}}/v1/chat/completions HTTP/1.1 +Content-Type: application/json + +{ + "messages": [ + { + "role": "user", + "content": "hello" + } + ], + "stream": true +} + +### llm gateway request (provider hint) +POST {{llm_endpoint}}/v1/chat/completions HTTP/1.1 +Content-Type: application/json +x-arch-llm-provider-hint: gpt-3.5-turbo-0125 + +{ + "messages": [ + { + "role": "user", + "content": "hello" + } + ] +}