Repository navigation
feat(sse): route GitHub Copilot Claude models through native /v1/messages - #7223
diegosouzapw merged 4 commits into
Conversation
…ages GitHub Copilot's /chat/completions and /responses endpoints never surface prompt-cache token counts (cached_tokens) for Claude models, and round-tripping Claude tool_use/tool_result/thinking content blocks through the OpenAI shape is lossy. Copilot also exposes an Anthropic-native /v1/messages shim that reports cached_tokens correctly and accepts native content blocks as-is. Tag each github registry claude-* model with targetFormat: "claude" so chatCore.ts translates the request to Anthropic-native shape before the executor ever sees it (the same mechanism opencode/zen's Qwen entries and opencode/go already use), and teach the github executor's buildUrl() / buildHeaders() to dispatch those models at the new messagesUrl (api.githubcopilot.com/v1/messages) with the required anthropic-version header. transformRequest() now skips its /chat/completions-only quirks (content-part flattening, trailing-assistant-prefill drop, the response_format-as-system-prompt workaround) for the native path — the first would destroy native tool_use/tool_result blocks, the prefill drop is unnecessary because the real Anthropic API supports assistant prefill, and the response_format workaround is superseded by the generic openai-to-claude translator's own JSON-mode handling. Co-authored-by: luoyide <ydhome.code@gmail.com> Inspired-by: decolua/9router#2608
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Aprovado. Revert-proof isola bem o fix: sem os 5 arquivos de produção, 5/10 testes caem exatamente nos pontos que o PR descreve (tool_use virando text, prefill perdido). Fiação do parâmetro 'model' em buildHeaders confirmada — o call site genérico em base.ts já suportava o 4º argumento, então não há regressão pros outros executors. Pronto para merge. |
…tions - Extract applyChatCompletionsOnlyQuirks() and resolveInitiatorHeader() out of GithubExecutor.transformRequest()/buildHeaders() so the two methods drop back under the complexity/cognitive-complexity ratchets (2058/891 -> 2056/890, matching the frozen baseline). No behavior change — same guards, just relocated. - Update 4 pre-existing unit tests that hard-coded now-native claude-* Copilot ids (claude-sonnet-4.5/4.6) to exercise the /chat/completions legacy path via an unregistered id (claude-sonnet-4), matching the sibling test already using that pattern. These ids now intentionally route to the native /v1/messages shim added by this PR, which correctly skips the /chat/completions-only workarounds these tests were built to verify — the native path's own coverage lives in github-copilot-claude-native-messages.test.ts. - Split the routing invariant test (copilot-gemini-claude-route-no-responses.test.ts) into a Claude case (expects /v1/messages) and a Gemini case (still expects /chat/completions), reflecting the intentional routing change.
Babysit summary — CI green ✅Reds fixed (2 root causes, both real, both caused by this PR's intentional routing change):
Also: merged Policy check — no collision. Gate: all green — Fast Quality Gates, 4/4 unit shards, Vitest, ESLint, Docs, Merge integrity, semgrep, dast-smoke. Ready for human review & merge — not merging. |
Dropped the claude-opus-4.6 reinstatement (contradicts diegosouzapw#7223/diegosouzapw#2821 with no new evidence; risks a production 400 on /v1/messages). Kept the gpt-5.6-sol/terra/luna additions, which already exist on the Codex provider. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* feat(github): refresh Copilot model catalog * feat(github): refresh Copilot model catalog (gpt-5.6 family) Dropped the claude-opus-4.6 reinstatement (contradicts #7223/#2821 with no new evidence; risks a production 400 on /v1/messages). Kept the gpt-5.6-sol/terra/luna additions, which already exist on the Codex provider. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: backryun <backryun@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…ages (diegosouzapw#7223) * feat(sse): route GitHub Copilot Claude models through native /v1/messages GitHub Copilot's /chat/completions and /responses endpoints never surface prompt-cache token counts (cached_tokens) for Claude models, and round-tripping Claude tool_use/tool_result/thinking content blocks through the OpenAI shape is lossy. Copilot also exposes an Anthropic-native /v1/messages shim that reports cached_tokens correctly and accepts native content blocks as-is. Tag each github registry claude-* model with targetFormat: "claude" so chatCore.ts translates the request to Anthropic-native shape before the executor ever sees it (the same mechanism opencode/zen's Qwen entries and opencode/go already use), and teach the github executor's buildUrl() / buildHeaders() to dispatch those models at the new messagesUrl (api.githubcopilot.com/v1/messages) with the required anthropic-version header. transformRequest() now skips its /chat/completions-only quirks (content-part flattening, trailing-assistant-prefill drop, the response_format-as-system-prompt workaround) for the native path — the first would destroy native tool_use/tool_result blocks, the prefill drop is unnecessary because the real Anthropic API supports assistant prefill, and the response_format workaround is superseded by the generic openai-to-claude translator's own JSON-mode handling. Co-authored-by: luoyide <ydhome.code@gmail.com> Inspired-by: decolua/9router#2608 * chore(changelog): fragment for diegosouzapw#7223 * fix(sse): green PR diegosouzapw#7223 CI — complexity ratchet + stale test expectations - Extract applyChatCompletionsOnlyQuirks() and resolveInitiatorHeader() out of GithubExecutor.transformRequest()/buildHeaders() so the two methods drop back under the complexity/cognitive-complexity ratchets (2058/891 -> 2056/890, matching the frozen baseline). No behavior change — same guards, just relocated. - Update 4 pre-existing unit tests that hard-coded now-native claude-* Copilot ids (claude-sonnet-4.5/4.6) to exercise the /chat/completions legacy path via an unregistered id (claude-sonnet-4), matching the sibling test already using that pattern. These ids now intentionally route to the native /v1/messages shim added by this PR, which correctly skips the /chat/completions-only workarounds these tests were built to verify — the native path's own coverage lives in github-copilot-claude-native-messages.test.ts. - Split the routing invariant test (copilot-gemini-claude-route-no-responses.test.ts) into a Claude case (expects /v1/messages) and a Gemini case (still expects /chat/completions), reflecting the intentional routing change. --------- Co-authored-by: luoyide <ydhome.code@gmail.com>
* feat(github): refresh Copilot model catalog * feat(github): refresh Copilot model catalog (gpt-5.6 family) Dropped the claude-opus-4.6 reinstatement (contradicts diegosouzapw#7223/diegosouzapw#2821 with no new evidence; risks a production 400 on /v1/messages). Kept the gpt-5.6-sol/terra/luna additions, which already exist on the Codex provider. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: backryun <backryun@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…ages (diegosouzapw#7223) * feat(sse): route GitHub Copilot Claude models through native /v1/messages GitHub Copilot's /chat/completions and /responses endpoints never surface prompt-cache token counts (cached_tokens) for Claude models, and round-tripping Claude tool_use/tool_result/thinking content blocks through the OpenAI shape is lossy. Copilot also exposes an Anthropic-native /v1/messages shim that reports cached_tokens correctly and accepts native content blocks as-is. Tag each github registry claude-* model with targetFormat: "claude" so chatCore.ts translates the request to Anthropic-native shape before the executor ever sees it (the same mechanism opencode/zen's Qwen entries and opencode/go already use), and teach the github executor's buildUrl() / buildHeaders() to dispatch those models at the new messagesUrl (api.githubcopilot.com/v1/messages) with the required anthropic-version header. transformRequest() now skips its /chat/completions-only quirks (content-part flattening, trailing-assistant-prefill drop, the response_format-as-system-prompt workaround) for the native path — the first would destroy native tool_use/tool_result blocks, the prefill drop is unnecessary because the real Anthropic API supports assistant prefill, and the response_format workaround is superseded by the generic openai-to-claude translator's own JSON-mode handling. Co-authored-by: luoyide <ydhome.code@gmail.com> Inspired-by: decolua/9router#2608 * chore(changelog): fragment for diegosouzapw#7223 * fix(sse): green PR diegosouzapw#7223 CI — complexity ratchet + stale test expectations - Extract applyChatCompletionsOnlyQuirks() and resolveInitiatorHeader() out of GithubExecutor.transformRequest()/buildHeaders() so the two methods drop back under the complexity/cognitive-complexity ratchets (2058/891 -> 2056/890, matching the frozen baseline). No behavior change — same guards, just relocated. - Update 4 pre-existing unit tests that hard-coded now-native claude-* Copilot ids (claude-sonnet-4.5/4.6) to exercise the /chat/completions legacy path via an unregistered id (claude-sonnet-4), matching the sibling test already using that pattern. These ids now intentionally route to the native /v1/messages shim added by this PR, which correctly skips the /chat/completions-only workarounds these tests were built to verify — the native path's own coverage lives in github-copilot-claude-native-messages.test.ts. - Split the routing invariant test (copilot-gemini-claude-route-no-responses.test.ts) into a Claude case (expects /v1/messages) and a Gemini case (still expects /chat/completions), reflecting the intentional routing change. --------- Co-authored-by: luoyide <ydhome.code@gmail.com>
* feat(github): refresh Copilot model catalog * feat(github): refresh Copilot model catalog (gpt-5.6 family) Dropped the claude-opus-4.6 reinstatement (contradicts diegosouzapw#7223/diegosouzapw#2821 with no new evidence; risks a production 400 on /v1/messages). Kept the gpt-5.6-sol/terra/luna additions, which already exist on the Codex provider. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: backryun <backryun@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Summary
/chat/completionsand/responsesendpoints never surface prompt-cache token counts (cached_tokens) for Claude models, and round-tripping Claudetool_use/tool_result/thinkingcontent blocks through the OpenAI shape is lossy.github(Copilot) provider now route through Copilot's Anthropic-native/v1/messagesshim instead, reusing OmniRoute's existing per-modeltargetFormatmechanism (the same one already used by opencode/zen's Qwen entries and opencode/go) so chatCore translates the request to Claude shape before the executor ever sees it./chat/completions(content-part flattening, trailing-assistant-prefill drop, theresponse_format-as-system-prompt workaround) are now skipped on the native path — the flattening in particular would otherwise destroy native tool_use/tool_result blocks.Attribution
Thanks to @yidecode for the original implementation.
Changes
open-sse/config/providers/registry/github/index.ts: addmessagesUrltransport field +targetFormat: "claude"on every claude-* model entry.open-sse/config/providers/shared.ts/open-sse/config/providerRegistry.ts/open-sse/executors/base.ts: thread the newmessagesUrlregistry field through toProviderConfig(mirrors howresponsesBaseUrlis threaded).open-sse/executors/github.ts:buildUrl()routes claude-targeted models tomessagesUrl;buildHeaders()adds the requiredanthropic-versionheader for those models;transformRequest()gates its /chat/completions-only transforms off the native path.tests/unit/provider-models-config.test.ts: updated the pre-existing registry-lineup assertion to reflect the new targetFormat.Test plan
tests/unit/github-copilot-claude-native-messages.test.ts— 10/10, fails before the change (asserted the old/chat/completionsURL, missing header, flattened content, dropped prefill) and passes afternode --import tsx/esm --teston the full set of github/registry-adjacent test files (108/108 pass, including the updatedprovider-models-config.test.tsand unmodifiedgithub-claude-reasoning-effort-granular.test.ts)npm run typecheck:core— cleannpx eslint(with suppressions) on all changed files — clean