Repository navigation
Conversation
decolua#2608 sent Claude models to Copilot's Anthropic-native /v1/messages shim by branching inside the executor: chatCore still resolved github's target format to "openai", and executeWithMessagesEndpoint translated openai -> claude on the way out and claude -> openai on the way back. That made targetFormat a lie, and a Claude-format client paid for it. A request from Claude Code / Zed (directly, or through a downstream Anthropic proxy) went claude -> openai -> claude, and request/claude-to-openai.js has no case for thinking blocks — its switch handles text, image, tool_use and tool_result only. The assistant turn's thinking block and signature were therefore dropped on the way in, so on every follow-up turn the model lost its own prior reasoning. It failed silently: Copilot accepts a history with no thinking block (verified), it only rejects a thinking block whose signature is missing. The provider now declares the routing decision where chatCore can see it: transport.resolveTargetFormat(model) -> "claude" | null resolved at request time from the model NAME, because Copilot's live catalog ships claude-* variants long before the static models[] list catches up (it currently serves claude-sonnet-5, claude-opus-4.8, claude-opus-4.8-fast and claude-fable-5, none of them listed). A static per-entry targetFormat would miss exactly those. Providers without the hook are unaffected — the helper returns null and the existing precedence chain is untouched. With the target format honest, a claude-source request short-circuits in translateRequest (sourceFormat === targetFormat) and reaches the shim untouched: thinking blocks and real signatures survive verbatim. prepareClaudeRequest still runs — it is gated on targetFormat === CLAUDE, outside the short-circuit — so decolua#2608's cache_control injection and its prompt-cache token counts are preserved. OpenAI-format clients still translate once, now in chatCore rather than the executor. executeWithMessagesEndpoint goes away entirely (-90 lines), along with its hand-rolled SSE transform and its duplicate _toolNameMap threading: the generic streaming handler already does both, keyed off the same target format. 由 Claude Code 辅助生成
golamrabbi696
added a commit
to golamrabbi696/EzRouter
that referenced
this pull request
Aug 12, 2026
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.
Follow-up to #2608 (same author). That PR sent Claude models to Copilot's Anthropic-native
/v1/messagesshim by branching inside the executor:chatCorestill resolved github's target format to"openai", andexecuteWithMessagesEndpointtranslated openai → claude on the way out and claude → openai on the way back.That made
targetFormata lie, and Claude-format clients paid for it.The bug
A request from Claude Code / Zed (directly, or through a downstream Anthropic proxy) went claude → openai → claude.
translator/request/claude-to-openai.jshas no case for thinking blocks — itsswitchhandlestext,image,tool_useandtool_resultonly. The assistant turn's thinking block and its signature were therefore dropped on the way in, so on every follow-up turn the model lost its own prior reasoning.It failed silently: Copilot accepts a history with no thinking block (verified), it only rejects a thinking block whose signature is missing. Same body, two routes:
The fix
The provider now declares the routing decision where
chatCorecan see it:Resolved at request time from the model name, because Copilot's live catalog ships
claude-*variants long before the staticmodels[]list catches up — it currently servesclaude-sonnet-5,claude-opus-4.8,claude-opus-4.8-fastandclaude-fable-5, none of them listed. A static per-entrytargetFormatwould miss exactly those, which is why #2608 avoided one; a runtime hook does not have that problem.Providers without the hook are unaffected — the helper returns
nulland the existing precedence chain is untouched.With the target format honest:
translateRequest(sourceFormat === targetFormat) and reaches the shim untouched — thinking blocks and real signatures survive verbatim.prepareClaudeRequeststill runs — it is gated ontargetFormat === CLAUDE, outside the short-circuit — so feat(github): route Claude models through Copilot's native /v1/messages #2608'scache_controlinjection and its prompt-cache token counts are preserved. There is a test pinning this.chatCorerather than the executor.executeWithMessagesEndpointgoes away entirely (-90 lines), along with its hand-rolled SSE transform and its duplicate_toolNameMapthreading: the generic streaming handler already does both, keyed off the same target format. Net -72 lines.Verification
tool_use,tool_result— returns 200.masterbaseline, zero regression.Scope
Deliberately does not touch
request/claude-to-openai.jsoropenai-to-claude.js— patching those would affect every provider that pivots through OpenAI, for a benefit this routing fix already delivers. Note thattests/translator/bugs-toClaude-context.test.jsalready has a red test (assistant reasoning_content becomes a thinking block) documenting that separate gap.OpenAI-format clients still cannot round-trip thinking — that format has nowhere to carry a signature. Inherent, not addressed here.
本 PR 由 Claude Code 辅助完成