[None][perf] disagg: optionally drop message history from generation request - #15448
Merged
lancelly merged 1 commit intoJun 18, 2026
Conversation
…request In disaggregated serving the orchestrator forwards each generation_only request to the generation worker carrying BOTH the full chat `messages` history AND the `prompt_token_ids` produced by the context server. The generation worker uses prompt_token_ids directly (it skips apply_chat_template) and only reads the last message for postprocessing, so the full conversation in `messages` is redundant on the gen side. At high concurrency the generation worker's single OpenAI-server process is GIL/event-loop bound on JSON-parsing + pydantic-validating that large request body; the messages history is ~40% of it. When the generation ingress is the binding constraint (the real, un-throttled operating point), trimming messages to the last turn measurably raises throughput and lowers TTFT (DSv4-Pro 6p1d c2048: req/s +2.4%, TTFT p50 -18% / p95 -6%). Tools are preserved (still used for tool-parser / guided decoding). Gated behind a new disagg config `gen_strip_message_history` (default off). It is only safe for text-only, non-harmony deployments: harmony/ gpt_oss generation workers ignore prompt_token_ids and re-tokenize the messages, and multimodal models carry image/audio parts in the history. Model type is fixed per disagg deployment, so the operator opts in via config rather than per-request detection. Signed-off-by: Lance Liao <108499334+lancelly@users.noreply.github.com>
Collaborator
Author
|
/bot run --disable-fail-fast |
Collaborator
|
PR_Github #54802 [ run ] triggered by Bot. Commit: |
Collaborator
|
PR_Github #54802 [ run ] completed with state
|
Collaborator
Author
|
/bot run --disable-fail-fast |
Collaborator
|
PR_Github #54844 [ run ] triggered by Bot. Commit: |
Collaborator
|
PR_Github #54844 [ run ] completed with state |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In disaggregated serving the orchestrator forwards each
generation_onlyrequest to thegeneration worker carrying both the full chat
messageshistory and theprompt_token_idsproduced by the context server. The generation worker usesprompt_token_idsdirectly (it skipsapply_chat_template) and only reads the last messagefor postprocessing, so the full conversation in
messagesis redundant on the gen side.At high concurrency the gen worker's single OpenAI-server process is GIL/event-loop bound on
JSON-parsing + pydantic-validating that large request body; the
messageshistory is ~40% ofit. Trimming
messagesto the last turn — when the gen ingress is the binding constraint —measurably raises throughput and lowers TTFT.
Change
gen_strip_message_history(default off) onDisaggServerConfig,wired through
extract_disagg_cfgas an explicit parameter (not**kwargs, so it is notinadvertently inherited into the per-server configs).
OpenAIDisaggregatedService._get_gen_requesttrimsmessages→messages[-1:]for
generation_onlyChatCompletionRequests (only whenprompt_token_idsis set). Toolsare preserved (still used for tool-parser / guided decoding).
Enable via the disagg YAML top level (same level as
hostname/schedule_style):Why config-gated (not unconditional)
A code audit found two GEN-side hazards an unconditional strip would hit on other model families:
chat_harmonyignoresprompt_token_idsand always re-tokenizesrequest.messages(openai_to_harmony_tokens); stripping → only the last turn is rendered →token/KV mismatch → corrupt output.
parse_chat_messages_coroutinesextractsmm_datafrommessages; strippingdrops history image/audio parts.
Both are per-model properties and a disagg cluster serves a single model, so a default-off config
flag (an operator assertion "text-only + non-harmony deployment") covers them with zero blast
radius. The standard
openai_chatpath is unaffected: it consumesprompt_token_ids, skipsapply_chat_template, and only readsmessages[-1](get_role()/last_message_content).Performance (DeepSeek-V4-Pro, 6p1d DEP4×6 + DEP8 GEN, MTP3, GB300, c2048, metrics-off)
gen_strip_message_historyThe gain appears at the un-throttled operating point where the gen-serving JSON-parse GIL is the
binding ingress constraint; it is absorbed when the system is decode-bound (closed-loop workload).
Test
extract_disagg_cfgparses the new top-level key ontoDisaggServerConfig.gen_strip_message_historyand does not leak it into the per-server configs; default (unset) =
False.