From fe5475b175b08fe2517c9e979a950133c83300ee Mon Sep 17 00:00:00 2001 From: Hiroshi Morishige Date: Sat, 15 Aug 2026 18:31:17 +0900 Subject: [PATCH 1/2] fix: preserve JSON object key order in proxied payloads serde_json's default Map is BTreeMap-backed, so every JSON object that passes through the router is re-serialized with alphabetized keys. Object key order is semantic for response_format.json_schema: order-enforcing structured-output backends (vLLM/xgrammar) use the properties declaration order as the generation order, so reordering silently changes what downstream models are forced to generate. Enable serde_json's preserve_order feature so payloads are forwarded with keys in the order the client sent them. Array order was already preserved. Signed-off-by: Hiroshi Morishige --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b8410e755..9b888c5ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2122,6 +2122,7 @@ version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ + "indexmap", "itoa", "memchr", "serde", diff --git a/Cargo.toml b/Cargo.toml index 07d133bb3..a0a57d964 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ parking_lot = "0.12" rand = "0.10" reqwest = { version = "0.13.4", default-features = false, features = ["json", "rustls", "stream"] } serde = { version = "1", features = ["derive"] } -serde_json = "1" +serde_json = { version = "1", features = ["preserve_order"] } switchyard-libsy = { path = "crates/libsy", version = "0.2.0" } switchyard-llm-client = { path = "crates/libsy-llm-client", version = "0.2.0" } switchyard-protocol = { path = "crates/protocol", version = "0.2.0" } From f4d30e3e9634b5767d6df2b799ba79835dae2d02 Mon Sep 17 00:00:00 2001 From: Hiroshi Morishige Date: Wed, 19 Aug 2026 09:49:27 +0900 Subject: [PATCH 2/2] chore: allow clippy::large_enum_variant on LlmResponse serde_json's preserve_order feature grows AggLlmResponse enough to trip clippy::large_enum_variant on LlmResponse (264 bytes). The size is acceptable here, so silence the lint rather than boxing the variant and changing the public API. Signed-off-by: Hiroshi Morishige --- crates/protocol/src/stream.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/protocol/src/stream.rs b/crates/protocol/src/stream.rs index b13b2c50c..7a70ca299 100644 --- a/crates/protocol/src/stream.rs +++ b/crates/protocol/src/stream.rs @@ -119,6 +119,7 @@ impl From for LlmResponseStreamEvent { /// Not `Clone` — the `Stream` variant owns a single-consumption stream. A buffered /// backend returns `Agg` directly; a streaming one returns `Stream` and the consumer /// drives it, folding to an [`AggLlmResponse`] when it needs the whole response. +#[allow(clippy::large_enum_variant)] pub enum LlmResponse { /// Live, single-consumption response stream. Stream(LlmResponseStream),