Skip to content

fix: extract system/developer messages in Claude Code semantic passthrough paths#2497

Open
unitythemaker wants to merge 3 commits into
diegosouzapw:mainfrom
unitythemaker:main
Open

fix: extract system/developer messages in Claude Code semantic passthrough paths#2497
unitythemaker wants to merge 3 commits into
diegosouzapw:mainfrom
unitythemaker:main

Conversation

@unitythemaker
Copy link
Copy Markdown

@unitythemaker unitythemaker commented May 21, 2026

Summary

Fixes a 400 error from Anthropic's Messages API when using Claude Code with OmniRoute. The semantic passthrough path (commit db8a2dc) preserved system/developer role messages in the messages array, but Anthropic requires them in the top-level system parameter.
Changes:

  • open-sse/services/claudeCodeCompatible.ts: cloneClaudeCodeCompatibleMessagesFromClaude() now filters system/developer roles; prepareClaudeCodeCompatibleSemanticBody() extracts them into top-level system
  • open-sse/handlers/chatCore.ts: Added inline system extraction in semantic passthrough branch; normalizeClaudeUpstreamMessages() now also extracts developer role

Related Issues

  • Closes # (N/A — regression from db8a2dc)

Validation

  • npm run lint — 0 errors (2747 pre-existing warnings)
  • npm run typecheck:core — clean
  • Unit tests — 27/27 pass (claude-code-compatible-request, claude-code-compatible-helpers, role-normalizer)
  • Full npm run test:coverage — skipped (suite times out >10 min locally)
  • Coverage is still >= 60% for statements, lines, functions, and branches
  • SonarQube PR analysis is green or any remaining issues are explicitly documented below

Tests Added Or Updated

No new test files added. Existing tests pass:

  • tests/unit/claude-code-compatible-request.test.ts — 10/10 pass
  • tests/unit/claude-code-compatible-helpers.test.ts — 6/6 pass
  • tests/unit/role-normalizer.test.ts — 11/11 pass

Coverage Notes

Changes are in open-sse/services/claudeCodeCompatible.ts and open-sse/handlers/chatCore.ts. Both files already have unit test coverage. The fix adds filtering logic that mirrors existing normalizeClaudeUpstreamMessages() behavior, which is tested.

Reviewer Notes

  • This is a regression fix for commit db8a2dc7 (preserve Claude Code messages on direct routes)
  • The fix touches the hot path in chatCore.ts but only adds a guard for system/developer role extraction — no new awaits or I/O
  • All 4 code paths (CC-compatible bridge, Claude passthrough non-semantic, Claude passthrough semantic, default) now consistently extract system messages
  • Manually validated on production server with Claude Code using cc/claude-opus-4-7 model

unitythemaker and others added 2 commits May 20, 2026 19:47
…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).
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread open-sse/handlers/chatCore.ts Outdated
Comment thread open-sse/handlers/chatCore.ts Outdated
Comment thread open-sse/handlers/chatCore.ts Outdated
Comment thread open-sse/handlers/chatCore.ts Outdated
Comment thread open-sse/handlers/chatCore.ts Outdated
Comment thread open-sse/services/claudeCodeCompatible.ts Outdated
: [];

const systemBlocks = normalizeClaudeSystemInput(claudeBody.system);
const systemFromMessages = extractCustomSystemBlocks(rawMessages);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

Comment thread open-sse/handlers/chatCore.ts Outdated
@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 21, 2026

Code Review Summary

Status: 1 Issue Found (Previously Resolved: 1 WARNING, 1 SUGGESTION) | Recommendation: Merge (incremental changes address previous findings)

Incremental Changes Review

The new commits address all previously identified issues:

Previous Issue Status Fix
WARNING: Tool role filtering dropped tool messages RESOLVED Filter changed from allowlist (user/assistant) to denylist (system/developer)
SUGGESTION: Duplicated system extraction logic RESOLVED Extracted to shared extractSystemMessagesToBody() helper

New Findings

No new issues found in incremental changes.

Previous Issues Carried Forward

Other Observations (not in diff - pre-existing)

Issues found in unchanged code that were flagged in previous review:

File Line Issue
open-sse/services/claudeCodeCompatible.ts 845 SUGGESTION: Inconsistent system extraction — extractCustomSystemBlocks() flattens structured content to text, unlike inline extraction in chatCore.ts which preserves block structure
open-sse/handlers/chatCore.ts 2580-2686 SUGGESTION: The normalizeClaudeUpstreamMessages function now handles developer role alongside system. Consider verifying that all callers of this function expect this behavior
Files Reviewed (2 files)
  • open-sse/handlers/chatCore.ts — Refactoring: extracted extractSystemMessagesToBody() helper, resolved duplication SUGGESTION
  • open-sse/services/claudeCodeCompatible.ts — Fixed tool role filtering WARNING

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
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.

1 participant