Skip to content

perf(protocols): stop default fields from defeating the serialize-once skip - #2267

Open
slin1237 wants to merge 1 commit into
mainfrom
perf/protocols-serde-fastpath-v2
Open

perf(protocols): stop default fields from defeating the serialize-once skip#2267
slin1237 wants to merge 1 commit into
mainfrom
perf/protocols-serde-fastpath-v2

Conversation

@slin1237

Copy link
Copy Markdown
Member

Description

Problem

#2139 added a skip that reuses the first serialization of an unmutated request body instead of re-encoding it. It never fires: several non-Option fields with constant defaults always serialize (for example GenerateRequest::return_hidden_states: false), so strip_default_sglang_fields finds them, marks every body mutated, and the router re-encodes the full body on every dispatch. On 35MB video bodies that is a full second serialization per request on the buffered path.

Solution

Skip serializing fields that sit at their strip-default value, so a default request round-trips byte-identical and the reuse skip actually fires:

  • GenerateRequest: return_hidden_states
  • ChatCompletionRequest: no_stop_trim, ignore_eos, continue_final_message, return_hidden_states (skip when false); separate_reasoning, stream_reasoning (skip when true - the strip removes these at any value, so their mere presence marked chat bodies mutated)
  • CompletionRequest: no_stop_trim, ignore_eos, return_hidden_states (same defect, same code path)
  • Plus a manual InputIds deserializer replacing the untagged enum, which removes serde's internal double-buffering of large token arrays

skip_special_tokens is deliberately untouched: the strip keeps it, so it never defeats the skip, and its presence is an asserted wire contract.

Changes

  • serde attributes + is_false/is_true helpers in crates/protocols (chat, completion, generate)
  • Manual InputIds Deserialize in common.rs
  • Reuse-proof tests wired through the real serialize_request_body path

Test Plan

  • Skip-fires proofs for generate, chat, and completion: returned bytes are pointer-equal to the first serialization (any strip/set_model edit necessarily changes bytes, so byte-equality proves mutated == false)
  • Roundtrip tests prove absent fields deserialize identically to omitted defaults, at default and non-default values
  • Known caveat documented: model-alias traffic always re-encodes (set_model marks mutated unconditionally) - mitigated since fix(router): pre-size dispatch serialization buffers; trim slack before freezing #2264 by borrowed-slice re-encode with a pre-sized buffer
  • cargo test -p openai-protocol 237 passed; cargo test -p smg --lib 1808 passed; api_tests 107, routing_tests 120, spec_test 96 green; clippy --all-targets -- -D warnings clean; fmt silent
Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

…heir first serialization

GenerateRequest always emitted "return_hidden_states":false, which the
sglang default strip then removed, so the unmutated-body fast path in
serialize_request_body never reused the first serialization for
/generate. ChatCompletionRequest (no_stop_trim, ignore_eos,
continue_final_message, return_hidden_states, separate_reasoning,
stream_reasoning) and CompletionRequest (no_stop_trim, ignore_eos,
return_hidden_states) defeated the same skip. Omit these fields at
their defaults: an absent field deserializes to the same default, and
the strip already removed the omitted values from outbound bodies.

InputIds was an untagged enum, so every tokenized request materialized
its token arrays twice through serde's private Content buffer. A manual
Deserialize probes the first element (number = Single, array = Batch)
and parses the rest in place. Serialize is unchanged.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Warning

Your free Security trial is over. An organization admin can activate billing to continue.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your current included review allowance is based on your included PR review attempts over the past 7 days.

Next review available in: 17 minutes

Limit details: You’ve used all 3 included reviews currently available. Your 71 included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a8412bd1-09fe-4e32-95ec-4eee97a6b177

📥 Commits

Reviewing files that changed from the base of the PR and between 43a4cbe and d02ec15.

📒 Files selected for processing (5)
  • crates/protocols/src/chat.rs
  • crates/protocols/src/common.rs
  • crates/protocols/src/completion.rs
  • crates/protocols/src/generate.rs
  • model_gateway/src/routers/http/request_body.rs

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added protocols Protocols crate changes model-gateway Model gateway crate changes labels Aug 21, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-tested performance optimization. The serde skip_serializing_if annotations correctly match each field's default, the manual InputIds deserializer handles all edge cases correctly, and the integration tests confirm the reuse path fires. No issues found.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

model-gateway Model gateway crate changes protocols Protocols crate changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant