diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 9a63afd5cc0..bfd5dff9f47 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -3,7 +3,7 @@ import json import re import time from collections.abc import Callable, Mapping, Sequence -from types import MappingProxyType +from types import BuiltinFunctionType, FunctionType, MappingProxyType from typing import TYPE_CHECKING, Any, ClassVar, Final, NoReturn, cast import httpx @@ -493,7 +493,26 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): @classmethod def get_config(cls, *, model: str | None = None): - config: Final = super().get_config() + # defaults configured on the base class (litellm.AnthropicConfig(max_tokens=...)) live on + # AnthropicConfig itself and must keep reaching subclass requests (vertex/azure claude) + base: Final = { + k: v + for k, v in AnthropicConfig.__dict__.items() + if not k.startswith("_") + and not isinstance( + v, + ( + FunctionType, + BuiltinFunctionType, + classmethod, + staticmethod, + property, + ), + ) + and v is not None + and not callable(v) + } + config: Final = {**base, **super().get_config()} # anthropic requires a default value for max_tokens if config.get("max_tokens") is None: @@ -2385,7 +2404,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): optional_params["tools"] = tools ## Load Config - config: Final = litellm.AnthropicConfig.get_config(model=model) + config: Final = self.get_config(model=model) for k, v in config.items(): if ( k not in optional_params diff --git a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py index 29253359003..fde1dd18752 100644 --- a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py +++ b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py @@ -11,6 +11,7 @@ from litellm.litellm_core_utils.prompt_templates.image_handling import RemoteMed from litellm.llms.base_llm.chat.transformation import LiteLLMLoggingObj from litellm.types.llms.openai import AllMessageValues from litellm.types.utils import ModelResponse +from litellm.utils import get_max_tokens from ....anthropic.chat.transformation import AnthropicConfig from .output_params_utils import sanitize_vertex_anthropic_output_params @@ -62,6 +63,16 @@ class VertexAIAnthropicConfig(AnthropicConfig): def should_strip_billing_metadata(self) -> bool: return True + @staticmethod + def get_max_tokens_for_model(model: str | None = None) -> int: + if model is not None: + vertex_key: Final = f"vertex_ai/{model}" + if vertex_key in litellm.model_cost: # pyright: ignore[reportUnknownMemberType] # registry is dynamically typed + vertex_max: Final = get_max_tokens(vertex_key) + if vertex_max is not None: + return vertex_max + return AnthropicConfig.get_max_tokens_for_model(model) + def _add_context_management_beta_headers(self, beta_set: set, context_management: dict) -> None: """ Add context_management beta headers to the beta_set. diff --git a/tests/unit/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_transformation.py b/tests/unit/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_transformation.py index 21bda6f0345..06cafbf4307 100644 --- a/tests/unit/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_transformation.py +++ b/tests/unit/llms/vertex_ai/vertex_ai_partner_models/anthropic/test_vertex_ai_partner_models_anthropic_transformation.py @@ -1185,3 +1185,44 @@ def test_refusal_stop_details_kept_through_vertex_anthropic_transform(): provider_fields: Final = result.choices[0].message.provider_specific_fields assert provider_fields["stop_reason"] == "refusal" assert provider_fields["stop_details"] == ANTHROPIC_DOCS_REFUSAL_MESSAGE["stop_details"] + +def test_vertex_ai_anthropic_versioned_model_default_max_tokens(local_model_cost_map): + config = VertexAIAnthropicConfig() + data = config.transform_request( + model="claude-haiku-4-5@20251001", + messages=[{"role": "user", "content": "hi"}], + optional_params={}, + litellm_params={}, + headers={}, + ) + assert data["max_tokens"] == 64000 + + unversioned = config.transform_request( + model="claude-haiku-4-5", + messages=[{"role": "user", "content": "hi"}], + optional_params={}, + litellm_params={}, + headers={}, + ) + assert unversioned["max_tokens"] == 64000 + + +def test_vertex_ai_anthropic_base_config_defaults_reach_subclass(local_model_cost_map): + from litellm import AnthropicConfig + + AnthropicConfig(max_tokens=123, temperature=0.5) + try: + data = VertexAIAnthropicConfig().transform_request( + model="claude-haiku-4-5@20251001", + messages=[{"role": "user", "content": "hi"}], + optional_params={}, + litellm_params={}, + headers={}, + ) + finally: + for attr in ("max_tokens", "temperature"): + if attr in AnthropicConfig.__dict__: + delattr(AnthropicConfig, attr) + + assert data["max_tokens"] == 123 + assert data["temperature"] == 0.5