Skip to content

feat(proxy): serve Anthropic-native /v1/models for Claude Code gateway discovery - #35455

Merged
yassin-berriai merged 18 commits into
BerriAI:litellm_internal_stagingfrom
Ar-maan05:feat-anthropic-native-v1-models-2
Aug 14, 2026
Merged

feat(proxy): serve Anthropic-native /v1/models for Claude Code gateway discovery#35455
yassin-berriai merged 18 commits into
BerriAI:litellm_internal_stagingfrom
Ar-maan05:feat-anthropic-native-v1-models-2

Conversation

@Ar-maan05

@Ar-maan05 Ar-maan05 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Re-lands #30273 (reverted during staging). No functional changes to the original; rebased onto current main and refactored so the diff is purely additive.

The problem

Claude Code 2.1.126+ added gateway model discovery: when ANTHROPIC_BASE_URL points at a gateway, it queries {base_url}/v1/models at startup and populates the /model picker with the discovered models. That discovery only parses the Anthropic-native Models API shape, so against litellm - which returns OpenAI's {id, object, created, owned_by} list - Claude Code finds nothing and the picker stays empty, even though /v1/messages already works.

The fix

This serves the Anthropic-native shape from the same /v1/models route via content negotiation on the anthropic-version header. Claude Code already sends that header for /v1/messages, so when it is present the endpoint returns the Anthropic Models envelope (type / display_name / created_at per entry, plus top-level has_more / first_id / last_id); otherwise the response is byte-for-byte the existing OpenAI shape, so aider and other OpenAI-compatible clients are unaffected.

Entries also carry the model's token limits when litellm knows them: max_input_tokens as-is, and the output budget as max_tokens, the name the Messages API uses for it. Both come from create_model_info_response, the same helper the OpenAI listing goes through, so cost-map values and router-configured overrides resolve identically in either shape

A separate endpoint was not used because Claude Code discovers at the gateway root's /v1/models, and a global config flag would break the OpenAI clients that share the route.

Design notes

  • Full list returned, client filters. Claude Code applies its own claude/anthropic id-prefix filter client-side, so no server-side filtering is imposed - a model aliased to claude-* pointing at any backend still shows up, which is the point for gateway users.
  • display_name falls back to the model id, the stable label a gateway can offer for arbitrary upstream models.
  • created_at is the ISO 8601 (Z-suffixed) form of the same timestamp the OpenAI shape already returns.
  • Hidden/unhealthy models are filtered before formatting in both the scope=expand and normal branches, exactly as for the OpenAI shape.
  • Both branches now build the OpenAI listing first and reshape it for Anthropic callers, so team name translation, hidden-model filtering and token limits can never drift between the two shapes. A model with no known limits simply omits the keys.
  • The formatter lives in litellm/llms/anthropic/common_utils.py per the repo rule against provider-specific code outside llms/; the request parameter is optional so existing direct (non-HTTP) callers of model_list are unaffected.

Tests

  • tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py: full envelope shape (per-entry type/display_name/created_at with a Z-suffixed ISO timestamp, top-level has_more/first_id/last_id, no object) and the empty-list case (first_id/last_id null).
  • tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py also pins the token limits: max_input_tokens passes through, max_output_tokens is renamed to max_tokens, and a model with only one known limit or none omits the rest.
  • tests/test_litellm/proxy/proxy_server/test_routes_models.py: drives GET /v1/models (and /models) with the anthropic-version header and asserts the negotiated Anthropic shape, including the limits for a model that has them and their absence for one that does not; the existing happy-path test pins that the default response stays OpenAI.

All three affected test files pass locally (215 tests). No server-side state or config changes.

@Ar-maan05 Ar-maan05 changed the title Feat anthropic native v1 models 2 feat(proxy): serve Anthropic-native /v1/models for Claude Code gateway discovery Aug 1, 2026
@greptile-apps

greptile-apps Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds Anthropic-native model discovery responses to the existing authenticated model-list routes while preserving the default OpenAI response.

  • Selects the Anthropic envelope when anthropic-version is present and formats model identifiers with ISO 8601 creation timestamps.
  • Adds route and formatter coverage for populated and empty model lists.
  • Extends the E2E transport with per-request GET timeouts and strengthens model-readiness polling for multi-worker propagation.

Confidence Score: 5/5

The PR appears safe to merge with no concrete changed-code failure identified.

The negotiated response is selected only for requests carrying the Anthropic-specific header, after the existing authorization and model-visibility filtering, while direct callers and default OpenAI clients retain their prior behavior.

Important Files Changed

Filename Overview
litellm/proxy/proxy_server.py Negotiates the model-list response from the request header after existing authorization and visibility filtering, with no accepted correctness issue.
litellm/llms/anthropic/common_utils.py Adds a focused formatter for the Anthropic Models API envelope, including safe empty-list handling.
tests/e2e/proxy_client.py Adds deadline-aware continuous model-visibility polling before E2E callers use newly created deployments.
tests/e2e/transport.py Adds an optional per-call GET timeout and propagates it through both HTTP and split transports.
tests/test_litellm/llms/anthropic/test_anthropic_common_utils.py Covers the populated and empty Anthropic model-list envelopes.
tests/test_litellm/proxy/proxy_server/test_routes_models.py Verifies header-based negotiation on both model-list route aliases while existing tests retain the default OpenAI contract.

Reviews (1): Last reviewed commit: "fix(proxy): make model_list request para..." | Re-trigger Greptile

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Ar-maan05
Ar-maan05 changed the base branch from litellm_internal_staging to main August 1, 2026 15:45
@codspeed-hq

codspeed-hq Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing Ar-maan05:feat-anthropic-native-v1-models-2 (b3248d7) with litellm_internal_staging (423b791)

Open in CodSpeed

@Ar-maan05
Ar-maan05 changed the base branch from main to litellm_internal_staging August 9, 2026 00:36
Comment thread litellm/proxy/proxy_server.py Outdated
@veria-ai

veria-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

Comment on lines +9028 to +9030
from litellm.llms.anthropic.common_utils import (
create_anthropic_model_list_response,
)
@connorgh14789

Copy link
Copy Markdown

@Ar-maan05 any update on this? We are running into the same issue in our org trying to implement LiteLLM

@yassin-berriai

Copy link
Copy Markdown
Contributor

This is green and merges cleanly, please review for merge. Worth adding max_input_tokens and max_tokens to the Anthropic entries, create_model_info_response already computes both

@yassin-berriai
yassin-berriai enabled auto-merge (squash) August 14, 2026 03:36
auto-merge was automatically disabled August 14, 2026 16:01

Head branch was pushed to by a user without write access

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
2 out of 3 committers have signed the CLA.

✅ yuneng-berri
✅ Ar-maan05
❌ yassin-berriai
You have signed the CLA already but the status is still pending? Let us recheck it.

@yassin-berriai
yassin-berriai merged commit e1f3d6e into BerriAI:litellm_internal_staging Aug 14, 2026
80 checks passed
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.

6 participants