fix: extract system/developer messages in Claude Code semantic passthrough paths#2497
fix: extract system/developer messages in Claude Code semantic passthrough paths#2497unitythemaker wants to merge 3 commits into
Conversation
…rough paths Commit db8a2dc added preserveClaudeMessages for Claude Code requests but never extracted system/developer role messages from the messages array into the top-level system parameter that Anthropic's Messages API requires. Three paths fixed: 1. claudeCodeCompatible.ts cloneClaudeCodeCompatibleMessagesFromClaude: filters out system/developer roles, keeping only user/assistant 2. claudeCodeCompatible.ts prepareClaudeCodeCompatibleSemanticBody: extracts system messages into top-level system param, removes from messages array 3. chatCore.ts semantic passthrough branch: inline system/developer extraction when normalizeClaudeUpstreamMessages is skipped Also fixed normalizeClaudeUpstreamMessages to extract developer role (previously only extracted system role).
There was a problem hiding this comment.
Code Review
This pull request updates the message handling logic to include the 'developer' role alongside the 'system' role, ensuring compatibility with newer Anthropic message formats. Changes include filtering and extracting these roles into the top-level system parameter in chatCore.ts and claudeCodeCompatible.ts. Feedback suggests using case-insensitive role comparisons to improve robustness and refactoring duplicated extraction logic into a shared helper function to enhance maintainability.
| : []; | ||
|
|
||
| const systemBlocks = normalizeClaudeSystemInput(claudeBody.system); | ||
| const systemFromMessages = extractCustomSystemBlocks(rawMessages); |
There was a problem hiding this comment.
SUGGESTION: Inconsistent system extraction — flattens structured content to plain text
extractCustomSystemBlocks() uses contentToText() which converts array content blocks to a plain string, losing structure (images, cache_control annotations, etc.).
Compare with the inline extraction in chatCore.ts:2755-2768 which preserves the original ClaudeContentBlock[] structure. This means the same system message may be handled differently depending on which code path processes it.
If structured system content (e.g., images, cache markers) is possible, consider using block-preserving extraction here too.
There was a problem hiding this comment.
This is pre-existing behavior in extractCustomSystemBlocks() predating this PR. Addressing it would require a separate refactor of that function — out of scope for this regression fix.
Code Review SummaryStatus: 1 Issue Found (Previously Resolved: 1 WARNING, 1 SUGGESTION) | Recommendation: Merge (incremental changes address previous findings) Incremental Changes ReviewThe new commits address all previously identified issues:
New FindingsNo new issues found in incremental changes. Previous Issues Carried ForwardOther Observations (not in diff - pre-existing)Issues found in unchanged code that were flagged in previous review:
Files Reviewed (2 files)
Reviewed by qwen3.6-plus · 267,561 tokens |
- Fix cloneClaudeCodeCompatibleMessagesFromClaude to exclude system/developer roles instead of allowlist user/assistant — preserves tool role messages - Add case-insensitive role comparisons throughout - Extract shared extractSystemMessagesToBody() helper in chatCore.ts, replacing the duplicated 40-line inline block in semantic passthrough path
Summary
Fixes a 400 error from Anthropic's Messages API when using Claude Code with OmniRoute. The semantic passthrough path (commit db8a2dc) preserved
system/developerrole messages in themessagesarray, but Anthropic requires them in the top-levelsystemparameter.Changes:
open-sse/services/claudeCodeCompatible.ts:cloneClaudeCodeCompatibleMessagesFromClaude()now filters system/developer roles;prepareClaudeCodeCompatibleSemanticBody()extracts them into top-levelsystemopen-sse/handlers/chatCore.ts: Added inline system extraction in semantic passthrough branch;normalizeClaudeUpstreamMessages()now also extractsdeveloperroleRelated Issues
Validation
npm run lint— 0 errors (2747 pre-existing warnings)npm run typecheck:core— cleannpm run test:coverage— skipped (suite times out >10 min locally)>= 60%for statements, lines, functions, and branchesTests Added Or Updated
No new test files added. Existing tests pass:
tests/unit/claude-code-compatible-request.test.ts— 10/10 passtests/unit/claude-code-compatible-helpers.test.ts— 6/6 passtests/unit/role-normalizer.test.ts— 11/11 passCoverage Notes
Changes are in
open-sse/services/claudeCodeCompatible.tsandopen-sse/handlers/chatCore.ts. Both files already have unit test coverage. The fix adds filtering logic that mirrors existingnormalizeClaudeUpstreamMessages()behavior, which is tested.Reviewer Notes
db8a2dc7(preserve Claude Code messages on direct routes)chatCore.tsbut only adds a guard forsystem/developerrole extraction — no new awaits or I/Occ/claude-opus-4-7model