Skip to content

fix: coalesce system messages into a single leading one at send time - #3

Merged
lxman merged 1 commit into
masterfrom
fix/qwen-single-system-message
Jul 31, 2026
Merged

fix: coalesce system messages into a single leading one at send time#3
lxman merged 1 commit into
masterfrom
fix/qwen-single-system-message

Conversation

@lxman

@lxman lxman commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #2 — Scaleway-hosted Qwen rejects requests with API error (400): "System message must be at the beginning." while Llama accepts the same payload. Qwen's chat template requires exactly one system message at position 0; the extension violated that in three places:

  1. Chat path (ChatWindowControl send): the per-turn auto-context (or no-solution note) was inserted as a system message just before the latest user message — a mid-conversation system message on essentially every request.
  2. Agent path: on every turn after the first, "User's current active file: …" was appended to the transcript as a system message.
  3. Agent transcript creation (AgentLoop.NewTranscript): with workspace orientation present, the transcript started with two consecutive system messages — the second, at index 1, is "not at the beginning" for strict templates.

Fix

ChatMessageNormalizer.CoalesceSystemMessages merges every system message (in order of appearance, joined by a blank line) into a single system message at index 0, leaving non-system messages — including tool-call metadata — untouched. It is applied at the request-build point in LLMService, so one code path covers chat, streaming, native-tool, and completion requests for every dialect. Permissive backends are unaffected; the persisted conversation history is never mutated (normalization is send-time only).

This also removes an inconsistency where TrimMessageHistory already hoisted system messages to the front, but only when trimming fired.

Testing

New tests/LocalModelIntegrator.Tests project (the VSIX project can't be referenced from a plain test project, so the transport-format-free sources are compile-linked): 7 tests covering mid-conversation merge, double-leading-system merge, canonical/no-system passthrough, non-mutation of history, tool-call metadata preservation, and blank-system dropping. All written first and watched fail. VSIX project builds with 0 errors.

🤖 Generated with Claude Code

Strict chat templates (Qwen among them) reject any request whose system
message is not the single first message - Scaleway-hosted Qwen returns
"API error (400): System message must be at the beginning". The extension
violated that in three places: the per-turn auto-context note (inserted
as a system message before the latest user turn), the agent path's
"current active file" note (appended mid-transcript), and the agent
transcript opening with two consecutive system messages when workspace
orientation exists.

ChatMessageNormalizer.CoalesceSystemMessages merges every system message
(in order of appearance, blank-line joined) into one system message at
index 0, leaving non-system messages and their tool-call metadata
untouched. It runs at the request-build point in LLMService, so all
chat, streaming, tool, and completion paths across every dialect are
covered, and the persisted conversation history is never mutated.

Fixes #2

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lxman
lxman merged commit 27311b7 into master Jul 31, 2026
2 checks passed
@lxman lxman mentioned this pull request Jul 31, 2026
@lxman
lxman deleted the fix/qwen-single-system-message branch July 31, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue with Qwen

1 participant