Skip to content

fix(cliproxyapi): detect Anthropic shape on minimal Capy bodies - #2192

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.0from
NomenAK:upstream/cliproxyapi-anthropic-shape-strong-detection
May 12, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.0from
NomenAK:upstream/cliproxyapi-anthropic-shape-strong-detection

Conversation

@NomenAK

@NomenAK NomenAK commented May 12, 2026

Copy link
Copy Markdown
Contributor

Summary

isAnthropicShape in the CliProxyApi executor missed minimal Anthropic bodies (messages[0].content as a plain string, no system block) and routed them to /v1/chat/completions instead of /v1/messages. The downstream symptom was Anthropic SDK clients receiving an OpenAI Chat Completions response shape and failing to parse.

Strengthen the detector with two more strong signals — top-level thinking field and top-level metadata.user_id — so the minimal-body case is also recognized.

Related Issues

Motivation

#2165 introduced the Anthropic-shape detector that routes Capy and other Claude SDK clients to CPA's /v1/messages. The detector required either :

  1. body.system !== undefined — Anthropic-only field
  2. messages[0].content is array — Anthropic content-block shape

Captured artifact for a minimal /v1/messages Capy call :

clientRawRequest.body = {
  "model": "claude-opus-4-7",
  "max_tokens": 500,
  "stream": false,
  "thinking": { "type": "adaptive", "display": "summarized" },
  "messages": [{ "role": "user", "content": "Dis bonjour." }]
}
  • body.system : absent ✗ (Capy omits when no system prompt)
  • messages[0].content : string ✗ (Anthropic allows both string and array per API docs)

Both indicators miss. Executor routes to /v1/chat/completions → CPA returns OpenAI Chat Completions shape → Anthropic SDK client sees choices[] instead of content[] and either drops the response or surfaces a parse error.

Changes

open-sse/executors/cliproxyapi.tsisAnthropicShape adds two strong indicators :

// Top-level `thinking` is Anthropic-only — OpenAI uses `reasoning` / `reasoning_effort`.
// Catches Capy minimal bodies where content is string and no system block exists.
if (b.thinking !== undefined) return true;

// Top-level `metadata.user_id` is the CC wire-image OAuth identifier;
// OpenAI request bodies don't carry it.
if (b.metadata && typeof b.metadata === "object" && (b.metadata as Record<string, unknown>).user_id !== undefined) return true;

Both are decisive ; either alone routes to /v1/messages.

Validation

  • Manual : pre-fix Capy minimal body returned chat.completion shape ; post-fix returns proper Anthropic shape with content: [{type:'text', text:'…'}].
  • Unit test : new it("does not strip OpenAI-shape bodies (no thinking, no system, string content)") — confirms genuine OpenAI bodies still skip the Anthropic strips when no Anthropic indicators are present.
  • All 37 existing CliProxyApi executor tests still pass.

🤖 Generated with Claude Code

Discovered post-deploy: simple Capy /v1/messages requests (string content,
no system block) were misdetected as OpenAI-shape and routed to
/v1/chat/completions instead of /v1/messages. CPA then responded with
chat.completion shape, leaking OpenAI shape to Anthropic SDK clients
and skipping the Anthropic CC wire-image cloak.

Strengthen isAnthropicShape with two more strong signals (any one is
decisive):
  - top-level `thinking` field (Anthropic-only; OpenAI uses `reasoning`)
  - top-level `metadata.user_id` (CC wire-image OAuth identifier)

These survive even on minimal bodies where messages[0].content is a
string and no system block is present.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@NomenAK
NomenAK requested a review from diegosouzapw as a code owner May 12, 2026 09:25

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

Copy link
Copy Markdown
Contributor

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 CliproxyapiExecutor to improve the detection of Anthropic-specific request bodies by checking for the thinking field and metadata.user_id. The unit tests have been updated to ensure that OpenAI-shaped bodies, including those with reasoning_effort, are correctly handled. I have no feedback to provide.

@NomenAK

NomenAK commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

Update after 2026-05-12 empirical bisect (11 variants × 2 turns + 5-turn stress against live Anthropic via CPA cloak):

The detection improvements in this PR (b.thinking, b.metadata.user_id as Anthropic-shape signals) remain valuable for routing — they fix the case of minimal Capy bodies that miss messages[0].content being an array and otherwise get mis-routed to /v1/chat/completions where CPA emits OpenAI-shape SSE that Anthropic SDK clients can't parse.

I'm closing the companion PRs that targeted the strip side-effects of this detection (#2194 mcp_ prose rewrite, #2195 metadata strip): the bisect confirmed Anthropic accepts those fields intact through the CPA cloak, so the strips were over-engineered preemptive defense that silently dropped valid client content. The detection itself (this PR) is orthogonal and stands.

The fork-side cleanup is in NomenAK/OmniRoute@cleanup/remove-over-engineered-strips (commits 4e9e534, 3166060, 4bb93c5) — keeping this PR open as-is for the detection signals.

@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks @NomenAK for the focused ClipProxyAPI fix. I synced the PR branch with release/v3.8.0, validated tests/unit/cliproxyapi-executor.test.ts locally, and will include this in the upcoming release.

@diegosouzapw
diegosouzapw merged commit 607ae5c into diegosouzapw:release/v3.8.0 May 12, 2026
2 checks passed
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…osouzapw#2192)

Integrated into release/v3.8.0 after syncing the contributor branch and validating tests/unit/cliproxyapi-executor.test.ts locally.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…osouzapw#2192)

Integrated into release/v3.8.0 after syncing the contributor branch and validating tests/unit/cliproxyapi-executor.test.ts locally.
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.

2 participants