[Bugfix]: preserve array-formatted system message content (#201) - #214
Open
wuhang2014 wants to merge 4 commits into
Open
[Bugfix]: preserve array-formatted system message content (#201)#214wuhang2014 wants to merge 4 commits into
wuhang2014 wants to merge 4 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a forwarding bug in the OpenAI Chat Completions protocol layer where system messages using OpenAI-style array content parts were deserialized as an empty string and therefore lost when re-serialized and forwarded upstream.
Changes:
- Change
ChatMessage::System.contentfromStringtoUserMessageContent(same polymorphic representation already used forUsermessages). - Update the custom
Deserializeimplementation forChatMessageto parse systemcontentviaserde_json::from_value. - Add a regression unit test ensuring system array content survives deserialize → serialize → reparse roundtrips; update benchmark fixtures accordingly.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/protocols/spec.rs | Makes system message content polymorphic (string or parts array) and adds a regression test for roundtripping array-formatted system content. |
| benches/request_processing.rs | Updates benchmark request constructors to use UserMessageContent::Text for system messages after the type change. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Signed-off-by: WU Hang <whlbx@hotmail.com>
wuhang2014
force-pushed
the
fix/201-system-array-content
branch
from
August 16, 2026 04:22
2728200 to
1b2424d
Compare
…t#201) Reusing UserMessageContent for system messages would also accept and forward image_url parts, but the OpenAI spec allows only text parts in system message arrays. Introduce role-specific SystemMessageContent (Text | Parts of text-only SystemContentPart) instead. A present but unparseable system content (image_url or other non-text parts) is now rejected with a deserialization error (400) rather than silently degrading to an empty string; missing/null content still defaults to empty text for backward compatibility. Adds test_chat_message_system_rejects_non_text_parts. Signed-off-by: WU Hang <whlbx@hotmail.com>
Signed-off-by: WU Hang <whlbx@hotmail.com>
wuhang2014
force-pushed
the
fix/201-system-array-content
branch
from
August 16, 2026 09:07
08b7d0e to
6d5ac8f
Compare
P2) SystemContentPart::Text only modeled the text field, so valid extension fields such as prompt_cache_breakpoint were silently dropped by the typed parse/reserialize forwarding round-trip. Capture unknown fields via #[serde(flatten)] and serialize them back unchanged. P1 (malformed content silently rewritten to "") is already addressed by the strict deserialization error path added in 7ae97ad; the new round-trip test also pins that metadata preservation regression. Signed-off-by: WU Hang <whlbx@hotmail.com>
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.
Purpose
Fixes #201: system messages with OpenAI-style array-formatted content (e.g.
[{"type":"text","text":"..."}]) were silently forwarded to upstream vLLM as an empty string"", so the system prompt was completely lost. String-format system content and array-format user content both worked correctly.Root cause:
ChatMessage's customDeserialize"system" branch coerced the content field withValue::as_str(), which returnsNonefor arrays, falling back to"".Fix: model
ChatMessage::System.contentwith a role-specificSystemMessageContent(untaggedText | Parts) whose parts are text-only (SystemContentPart), and deserialize it viaserde_json::from_value, so both content forms round-trip through the router's parse-and-reserialize forwarding path. Serialization of the untagged enum already handles string vs array.Strict OpenAI compatibility: system message arrays may only contain text parts. A present but unparseable system content (e.g.
image_urlparts) is rejected at deserialization time and surfaces as a 4xx (422 via axum'sJsonextractor) instead of silently degrading to an empty string; missing/null content still defaults to empty text for backward compatibility. User messages remain permissive (UserMessageContentwithimage_urlsupport).Test Plan
Added unit tests in
src/protocols/spec.rs:test_chat_message_system_array_content_roundtrip— deserialize → re-serialize → reparse of array-formatted system contenttest_chat_message_system_rejects_non_text_parts—image_url(and other non-text) parts are rejected with a deserialization error; missing/null content still defaults to empty textEnd-to-end verification: router built with this change, mock upstream worker echoing the received body,
POST /v1/chat/completions.Test Result
Rust unit/integration tests and CI-grade clippy all pass (only pre-existing failure:
test_openai_router_healthreturns 503 on macOS, also fails on pristinemain).End-to-end forwarded content, before → after:
""[{"type":"text","text":"You are GLM-5.2."}]"You are GLM-5.2.""You are GLM-5.2."""[{"type":"text","text":"hello array user"}][{"type":"text","text":"hello array user"}]Essential Elements of an Effective PR Description Checklist