fix(bedrock): send an application inference profile's effort when its base_model is set

This commit is contained in:
mateo-berri 2026-11-03 20:50:30 -08:00
parent b76bbd3c92
commit 731bbed432
3 changed files with 24 additions and 10 deletions

View file

@ -646,6 +646,7 @@ class LiteLLMMessagesToCompletionTransformationHandler:
request_data["output_config"] = extra_kwargs["output_config"]
custom_llm_provider: Final = extra_kwargs.get("custom_llm_provider")
base_model: Final = extra_kwargs.get("base_model")
(
openai_request,
tool_name_mapping,
@ -653,6 +654,7 @@ class LiteLLMMessagesToCompletionTransformationHandler:
request_data,
custom_llm_provider=custom_llm_provider if isinstance(custom_llm_provider, str) else None,
drop_params=drops_unsupported_params(extra_kwargs.get("drop_params")),
base_model=base_model if isinstance(base_model, str) else None,
)
if openai_request is None:

View file

@ -16,7 +16,7 @@ from litellm.llms.anthropic.messages.utils import (
json_copy_or_original,
prompt_cache_key_from_user_id,
)
from litellm.llms.bedrock.common_utils import is_bedrock_application_inference_profile_arn
from litellm.llms.bedrock.common_utils import bedrock_capability_model, is_bedrock_application_inference_profile_arn
from litellm.responses.utils import auto_reasoning_summary
# OpenAI has a 64-character limit for function/tool names
@ -256,7 +256,12 @@ class AnthropicAdapter:
return result
def translate_completion_input_params_with_tool_mapping(
self, kwargs, *, custom_llm_provider: str | None = None, drop_params: bool = True
self,
kwargs,
*,
custom_llm_provider: str | None = None,
drop_params: bool = True,
base_model: str | None = None,
) -> tuple[ChatCompletionRequest | None, dict[str, str]]:
"""
Translate Anthropic request params to OpenAI format, returning tool name mapping.
@ -291,6 +296,7 @@ class AnthropicAdapter:
anthropic_message_request=request_body,
custom_llm_provider=custom_llm_provider,
drop_params=drop_params,
base_model=base_model,
)
return translated_body, tool_name_mapping
@ -369,8 +375,8 @@ _BlockT: Final = TypeVar("_BlockT", bound=Mapping[str, object])
_CLAUDE_OVER_REASONING_EFFORT_PROVIDERS: Final = frozenset({LlmProviders.KYMA.value})
UNRESOLVED_ARN_EFFORT_WITHOUT_THINKING_WARNING: Final = (
"Dropping `output_config` for model=%s under drop_params: an application inference profile ARN does not name "
"its model, so LiteLLM cannot tell whether it takes an effort without a `thinking` block. Turn off drop_params "
"on the deployment to send it."
"its model, so LiteLLM cannot tell whether it takes an effort without a `thinking` block. Set `base_model` on "
"the deployment (for example `us.anthropic.claude-opus-4-8`) to send it."
)
@ -1210,6 +1216,7 @@ class LiteLLMAnthropicMessagesAdapter:
anthropic_message_request: AnthropicMessagesRequest,
new_kwargs: ChatCompletionRequest,
drop_params: bool,
base_model: str | None,
) -> None:
"""Forward the effort-bearing part of ``output_config`` to a Bedrock Claude target.
@ -1231,7 +1238,7 @@ class LiteLLMAnthropicMessagesAdapter:
if (
drop_params
and not anthropic_message_request.get("thinking")
and is_bedrock_application_inference_profile_arn(model)
and is_bedrock_application_inference_profile_arn(bedrock_capability_model(model, base_model))
):
litellm.verbose_logger.warning(UNRESOLVED_ARN_EFFORT_WITHOUT_THINKING_WARNING, model)
return
@ -1283,6 +1290,7 @@ class LiteLLMAnthropicMessagesAdapter:
custom_llm_provider: str | None = None,
preserve_midturn_system: bool = False,
drop_params: bool = True,
base_model: str | None = None,
) -> tuple[ChatCompletionRequest, dict[str, str]]:
"""
This is used by the beta Anthropic Adapter, for translating anthropic `/v1/messages` requests to the openai format.
@ -1341,6 +1349,7 @@ class LiteLLMAnthropicMessagesAdapter:
anthropic_message_request=anthropic_message_request,
new_kwargs=new_kwargs,
drop_params=drop_params,
base_model=base_model,
)
## CONVERT STOP_SEQUENCES
self._translate_stop_sequences_to_openai(

View file

@ -5560,17 +5560,19 @@ _APPLICATION_INFERENCE_PROFILE: Final = (
@pytest.mark.asyncio
@pytest.mark.parametrize(
("thinking", "drop_params", "forwards_effort"),
("thinking", "drop_params", "base_model", "forwards_effort"),
[
pytest.param(None, False, True, id="no_thinking"),
pytest.param(None, True, False, id="no_thinking_drop_params"),
pytest.param({"type": "enabled", "budget_tokens": 1024}, True, True, id="thinking_drop_params"),
pytest.param(None, False, None, True, id="no_thinking"),
pytest.param(None, True, None, False, id="no_thinking_drop_params"),
pytest.param({"type": "enabled", "budget_tokens": 1024}, True, None, True, id="thinking_drop_params"),
pytest.param(None, True, _CONVERSE_EFFORT_MODEL, True, id="no_thinking_drop_params_base_model"),
],
)
async def test_bedrock_application_inference_profile_loses_the_effort_only_without_thinking_under_drop_params(
async def test_bedrock_application_inference_profile_loses_the_effort_only_when_unresolved_without_thinking(
_converse_route: respx.Route,
thinking: Mapping[str, object] | None,
drop_params: bool,
base_model: str | None,
forwards_effort: bool,
) -> None:
await litellm.anthropic.messages.acreate(
@ -5580,6 +5582,7 @@ async def test_bedrock_application_inference_profile_loses_the_effort_only_witho
output_config={"effort": "high", "format": _CITY_FORMAT},
drop_params=drop_params,
**({"thinking": thinking} if thinking else {}),
**({"base_model": base_model} if base_model else {}),
)
sent: Final = _sent_converse_fields(_converse_route)