feat(github_copilot): route /v1/messages to Copilot native Anthropic endpoint - #31802
Conversation
…endpoint Add a GitHub Copilot Anthropic Messages transformation that routes supported Claude models through the native /v1/messages endpoint. This covers request URL construction, default headers, and supported model metadata.
Tighten the Anthropic Messages environment validation and web search interception behavior after review feedback. Avoid treating non-web-search requests as web-search-only paths.
Apply Black formatting to the GitHub Copilot Anthropic Messages tests.
…pic Messages Add coverage for ProviderConfigManager dispatch when GitHub Copilot models use the Anthropic Messages API, including non-Anthropic models returning no config.
Set the messages-proxy interaction header for GitHub Copilot Anthropic Messages requests so /v1/messages uses the expected Copilot intent.
Replace legacy typing generics in the GitHub Copilot Anthropic Messages transformation so the strict Ruff budget gate stays within its ceiling.
|
Generated by Claude Code |
Greptile SummaryAdds
Confidence Score: 5/5Safe to merge; the new routing path is isolated to GitHub Copilot Claude models on /v1/messages, existing providers are untouched, and the auth pinning prevents token leakage. The implementation is well-contained and thoroughly tested with mock-only unit tests. The routing guard in utils.py is slightly broader than the three models whose metadata was updated, but this results in Copilot returning a model-not-supported error rather than any data corruption or security issue. litellm/utils.py — the "claude" in model_lower routing guard routes all six Copilot Claude models through the new config, while only three received the /v1/messages metadata update.
|
| Filename | Overview |
|---|---|
| litellm/llms/github_copilot/messages/transformation.py | New GithubCopilotAnthropicMessagesConfig class; correctly pins api_base to the authenticated Copilot host, normalises trailing slashes, and overrides beta-header filtering and web-search dispatch. |
| litellm/utils.py | Adds GITHUB_COPILOT branch to _get_provider_anthropic_messages_config_cached; routing guard ("claude" in model_lower) is broader than the three models that received /v1/messages in supported_endpoints metadata. |
| litellm/integrations/websearch_interception/handler.py | Short-circuit logic now checks handles_web_search_natively() instead of testing for config presence alone; correctly keeps Copilot on the short-circuit path even with its new messages config. |
| litellm/llms/base_llm/anthropic_messages/transformation.py | Adds handles_web_search_natively() hook to BaseAnthropicMessagesConfig with a safe default of True; no behaviour change for existing providers. |
| litellm/model_prices_and_context_window_backup.json | Adds /v1/messages to supported_endpoints for three Claude models (haiku-4.5, opus-4.5, sonnet-4.5); three other Copilot Claude models (opus-4.6-fast, opus-41, sonnet-4) are not updated but will still be routed through the new config. |
| tests/test_litellm/llms/github_copilot/messages/test_github_copilot_messages_transformation.py | Comprehensive mock-only unit tests covering URL construction, header injection, beta-header filtering, auth errors, and provider config dispatch; no real network calls. |
Reviews (6): Last reviewed commit: "test(github_copilot): remove dead branch..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…n /v1/messages URL Address review feedback on the Copilot native Anthropic messages path. Replace the hardcoded LlmProviders.GITHUB_COPILOT check in the web-search interception handler with a handles_web_search_natively() method on BaseAnthropicMessagesConfig (default True), overridden to False in GithubCopilotAnthropicMessagesConfig. Provider-specific behavior now lives in llms/ and the handler stays provider-agnostic, so a future provider in the same situation needs no carve-out here. In get_complete_url, reuse the already-resolved api_base returned by validate_anthropic_messages_environment instead of reading the authenticator a second time, removing redundant I/O and the mid-request inconsistency window. The caller-supplied base is still discarded in validate, which is the security boundary. Normalize a trailing slash on the base in both methods so a tenant-specific host never yields a double-slash //v1/messages URL.
|
Thanks for the review. Addressed all three findings in ada5fcc Provider-specific check outside
Double-slash URL: both methods now Added regression tests for the new method contract (Copilot Generated by Claude Code |
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Beta headers stripped for Copilot
- Overrode should_filter_anthropic_beta_headers() to return False on GithubCopilotAnthropicMessagesConfig so injected anthropic-beta values (context_management, structured outputs, etc.) reach Copilot's native /v1/messages verbatim instead of being dropped by the provider-scoped filter, and added a regression test.
You can send follow-ups to the cloud agent here.
The Copilot config inherited should_filter_anthropic_beta_headers()==True from BaseAnthropicMessagesConfig, so update_headers_with_filtered_beta stripped every anthropic-beta value after validate_anthropic_messages_environment injected them (github_copilot has no mapping in anthropic_beta_headers_config.json). That silently disabled header-gated features like context_management and structured outputs on the native passthrough. Override the hook to False, matching OpenAILikeAnthropicMessagesConfig.
|
|
|
Bugbot's autofix in 210bc94 is correct; Copilot's Generated by Claude Code |
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 210bc94. Configure here.
The anthropic-beta filtering test guarded the fix with an if branch on should_filter_anthropic_beta_headers(), which is always False, so the branch was unreachable. Replace it with a direct assertion that running the provider-scoped filter for github_copilot strips every beta value, proving why the override is load-bearing and catching a regression that flips it back on.
|
Addressed the test quality nit in 54bff77; the beta-header regression test had an unreachable Generated by Claude Code |
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 54bff77. Configure here.
|
On the note about the The I am deliberately not widening the metadata here: declaring Generated by Claude Code |
…endpoint (BerriAI#31802) * feat(github_copilot): route /v1/messages to Copilot native Anthropic endpoint Add a GitHub Copilot Anthropic Messages transformation that routes supported Claude models through the native /v1/messages endpoint. This covers request URL construction, default headers, and supported model metadata. * fix(github_copilot): address PR review feedback Tighten the Anthropic Messages environment validation and web search interception behavior after review feedback. Avoid treating non-web-search requests as web-search-only paths. * style(github_copilot): apply black formatting Apply Black formatting to the GitHub Copilot Anthropic Messages tests. * test(github_copilot): cover ProviderConfigManager dispatch for Anthropic Messages Add coverage for ProviderConfigManager dispatch when GitHub Copilot models use the Anthropic Messages API, including non-Anthropic models returning no config. * fix(github_copilot): apply messages-proxy intent header to /v1/messages Set the messages-proxy interaction header for GitHub Copilot Anthropic Messages requests so /v1/messages uses the expected Copilot intent. * fix(github_copilot): use modern generic annotations Replace legacy typing generics in the GitHub Copilot Anthropic Messages transformation so the strict Ruff budget gate stays within its ceiling. * refactor(github_copilot): decouple web-search short-circuit and harden /v1/messages URL Address review feedback on the Copilot native Anthropic messages path. Replace the hardcoded LlmProviders.GITHUB_COPILOT check in the web-search interception handler with a handles_web_search_natively() method on BaseAnthropicMessagesConfig (default True), overridden to False in GithubCopilotAnthropicMessagesConfig. Provider-specific behavior now lives in llms/ and the handler stays provider-agnostic, so a future provider in the same situation needs no carve-out here. In get_complete_url, reuse the already-resolved api_base returned by validate_anthropic_messages_environment instead of reading the authenticator a second time, removing redundant I/O and the mid-request inconsistency window. The caller-supplied base is still discarded in validate, which is the security boundary. Normalize a trailing slash on the base in both methods so a tenant-specific host never yields a double-slash //v1/messages URL. * fix(github_copilot): forward anthropic-beta headers on /v1/messages The Copilot config inherited should_filter_anthropic_beta_headers()==True from BaseAnthropicMessagesConfig, so update_headers_with_filtered_beta stripped every anthropic-beta value after validate_anthropic_messages_environment injected them (github_copilot has no mapping in anthropic_beta_headers_config.json). That silently disabled header-gated features like context_management and structured outputs on the native passthrough. Override the hook to False, matching OpenAILikeAnthropicMessagesConfig. * test(github_copilot): remove dead branch in beta-header regression test The anthropic-beta filtering test guarded the fix with an if branch on should_filter_anthropic_beta_headers(), which is always False, so the branch was unreachable. Replace it with a direct assertion that running the provider-scoped filter for github_copilot strips every beta value, proving why the override is load-bearing and catching a regression that flips it back on. --------- Co-authored-by: ririnto <ririnto@kakao.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…endpoint (BerriAI#31802) * feat(github_copilot): route /v1/messages to Copilot native Anthropic endpoint Add a GitHub Copilot Anthropic Messages transformation that routes supported Claude models through the native /v1/messages endpoint. This covers request URL construction, default headers, and supported model metadata. * fix(github_copilot): address PR review feedback Tighten the Anthropic Messages environment validation and web search interception behavior after review feedback. Avoid treating non-web-search requests as web-search-only paths. * style(github_copilot): apply black formatting Apply Black formatting to the GitHub Copilot Anthropic Messages tests. * test(github_copilot): cover ProviderConfigManager dispatch for Anthropic Messages Add coverage for ProviderConfigManager dispatch when GitHub Copilot models use the Anthropic Messages API, including non-Anthropic models returning no config. * fix(github_copilot): apply messages-proxy intent header to /v1/messages Set the messages-proxy interaction header for GitHub Copilot Anthropic Messages requests so /v1/messages uses the expected Copilot intent. * fix(github_copilot): use modern generic annotations Replace legacy typing generics in the GitHub Copilot Anthropic Messages transformation so the strict Ruff budget gate stays within its ceiling. * refactor(github_copilot): decouple web-search short-circuit and harden /v1/messages URL Address review feedback on the Copilot native Anthropic messages path. Replace the hardcoded LlmProviders.GITHUB_COPILOT check in the web-search interception handler with a handles_web_search_natively() method on BaseAnthropicMessagesConfig (default True), overridden to False in GithubCopilotAnthropicMessagesConfig. Provider-specific behavior now lives in llms/ and the handler stays provider-agnostic, so a future provider in the same situation needs no carve-out here. In get_complete_url, reuse the already-resolved api_base returned by validate_anthropic_messages_environment instead of reading the authenticator a second time, removing redundant I/O and the mid-request inconsistency window. The caller-supplied base is still discarded in validate, which is the security boundary. Normalize a trailing slash on the base in both methods so a tenant-specific host never yields a double-slash //v1/messages URL. * fix(github_copilot): forward anthropic-beta headers on /v1/messages The Copilot config inherited should_filter_anthropic_beta_headers()==True from BaseAnthropicMessagesConfig, so update_headers_with_filtered_beta stripped every anthropic-beta value after validate_anthropic_messages_environment injected them (github_copilot has no mapping in anthropic_beta_headers_config.json). That silently disabled header-gated features like context_management and structured outputs on the native passthrough. Override the hook to False, matching OpenAILikeAnthropicMessagesConfig. * test(github_copilot): remove dead branch in beta-header regression test The anthropic-beta filtering test guarded the fix with an if branch on should_filter_anthropic_beta_headers(), which is always False, so the branch was unreachable. Replace it with a direct assertion that running the provider-scoped filter for github_copilot strips every beta value, proving why the override is load-bearing and catching a regression that flips it back on. --------- Co-authored-by: ririnto <ririnto@kakao.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Relevant issues
This is a copy of #28054 by @ririnto, rebased onto the current
litellm_internal_stagingso it can run through our CI. All credit for the implementation goes to @ririnto; this branch only resolves the merge conflict against the moved base and normalizes formatting to the current ruffthinkingend-to-end by avoiding the chat-completions conversion that strips itcontent_block_*events are preservedreasoning_contentthinking->reasoning_efforton the chat path, this PR keeps Anthropic semantics intact via Copilot's native/v1/messagesLinear ticket
n/a
Pre-Submission checklist
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewScreenshots / Proof of Fix
Direct upstream verification from the original PR (independent of LiteLLM, to isolate Copilot's behaviour):
This PR makes LiteLLM produce the "After" response when a client calls
POST /v1/messagesagainst agithub_copilotClaude model. Unit tests cover config wiring, URL construction, header merging precedence, and auth error mappingType
New Feature
Changes
litellm/llms/github_copilot/messages/__init__.pylitellm/llms/github_copilot/messages/transformation.pyGithubCopilotAnthropicMessagesConfig(AnthropicMessagesConfig)validate_anthropic_messages_environment(Copilot auth + integration headers +anthropic-version) andget_complete_url(/v1/messages)api_baseis intentionally ignored so the Copilot bearer token can never be routed to a caller-controlled URLlitellm/integrations/websearch_interception/handler.py; keep the web-search short-circuit forgithub_copiloteven though it now has aBaseAnthropicMessagesConfig, since Copilot does not handleweb_searchtools nativelylitellm/utils.py;ProviderConfigManager._get_provider_anthropic_messages_config_cached()returns the new config whenprovider == GITHUB_COPILOT and "claude" in model_lowerlitellm/model_prices_and_context_window_backup.json; add/v1/messagestosupported_endpointsfor the 3 base Claude models this routing enablestests/test_litellm/llms/github_copilot/messages/Generated by Claude Code
Note
Medium Risk
Changes request routing and auth for GitHub Copilot Claude on /v1/messages (including deliberate api_base pinning); behavior is well-tested but affects thinking, streaming, beta headers, and web-search interception for that provider.
Overview
Adds
GithubCopilotAnthropicMessagesConfigsogithub_copilotClaude models onPOST /v1/messagesgo to Copilot’s native/v1/messagespath (auth,messages-proxyheaders, URL building) instead of the chat-completions conversion that drops thinking and native streaming blocks.ProviderConfigManagerreturns this config when the provider isGITHUB_COPILOTand the model name containsclaude; non-Claude Copilot models stay off this path. Caller-suppliedapi_baseis ignored in validation so the Copilot bearer token is only sent to the authenticated Copilot host.Copilot is treated as an Anthropic passthrough for beta headers:
should_filter_anthropic_beta_headers()is overridden toFalseso injectedanthropic-betavalues (e.g. context management, structured outputs) are not stripped. Model metadata now lists/v1/messageson the affected Claude Copilot entries.Web-search interception no longer skips short-circuit merely because an Anthropic Messages config exists; it checks new
handles_web_search_natively()(default True on the base config, False for Copilot) so Copilot still gets the synthetic web-search short-circuit even with the new messages config.Reviewed by Cursor Bugbot for commit 54bff77. Bugbot is set up for automated code reviews on this repo. Configure here.