Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion litellm-rust/crates/model-catalog/src/model_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ pub struct ModelInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_bedrock_runtime_chat_completions_response_format: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_bedrock_runtime_chat_completions_tools_with_reasoning: Option<bool>,
pub supports_bedrock_chat_completions_tools_with_reasoning: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supports_computer_use: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down
66 changes: 66 additions & 0 deletions litellm/litellm_core_utils/responses_api_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import annotations

from collections.abc import Mapping, Sequence
from types import MappingProxyType
from typing import Final, Protocol, cast, runtime_checkable


@runtime_checkable
class _FunctionToolLike(Protocol):
type: str


def has_function_tool(tools: object) -> bool:
if not isinstance(tools, Sequence):
return False
typed_tools: Final = cast(Sequence[object], tools) # cast-ok: a runtime Sequence contains Python objects
return any(_is_function_tool(tool) for tool in typed_tools)


def _is_function_tool(tool: object) -> bool:
if not isinstance(tool, Mapping):
return isinstance(tool, _FunctionToolLike) and tool.type == "function"
tool_mapping: Final = cast(Mapping[str, object], tool) # cast-ok: OpenAI tool mappings have string keys
function: Final = tool_mapping.get("function")
return tool_mapping.get("type") == "function" and (isinstance(function, Mapping) or "name" in tool_mapping)


def reasoning_effort_is_active(
reasoning_effort: object,
supports_none_reasoning_effort: bool | None = None,
) -> bool:
if isinstance(reasoning_effort, Mapping):
effort_mapping: Final = cast( # cast-ok: reasoning payload keys are strings
Mapping[str, object], reasoning_effort
)
return (
effort_mapping.get("effort") != "none"
or effort_mapping.get("summary") is not None
or supports_none_reasoning_effort is False
)
return reasoning_effort != "none" or supports_none_reasoning_effort is False
Comment thread
cursor[bot] marked this conversation as resolved.


def peek_reasoning_summary_aliases(optional_params: Mapping[str, object]) -> object | None:
if "reasoningSummary" in optional_params:
return optional_params["reasoningSummary"]
if "reasoning_summary" in optional_params:
return optional_params["reasoning_summary"]
extra_body: Final = optional_params.get("extra_body")
if isinstance(extra_body, Mapping):
extra_body_mapping: Final = cast( # cast-ok: extra_body uses JSON object keys
Mapping[str, object], extra_body
)
if "reasoningSummary" in extra_body_mapping:
return extra_body_mapping["reasoningSummary"]
if "reasoning_summary" in extra_body_mapping:
return extra_body_mapping["reasoning_summary"]
return None


def filter_additional_drop_params(
request_params: Mapping[str, object],
additional_drop_params: Sequence[str] | None,
) -> Mapping[str, object]:
dropped: Final = frozenset(additional_drop_params or ())
return MappingProxyType({key: value for key, value in request_params.items() if key not in dropped})
39 changes: 16 additions & 23 deletions litellm/llms/bedrock/chat/chat_completions/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from typing import TYPE_CHECKING, Final, Literal

import httpx
from pydantic import TypeAdapter
from typing_extensions import assert_never

import litellm
Expand All @@ -29,7 +30,12 @@
)
from litellm.llms.base_llm.chat.transformation import BaseLLMException
from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM
from litellm.llms.bedrock.common_utils import BedrockError, split_bedrock_region_path
from litellm.llms.bedrock.common_utils import (
BedrockError,
bedrock_chat_completions_serves_tools_with_reasoning,
split_bedrock_region_path,
with_max_completion_tokens,
)
from litellm.llms.openai.chat.gpt_transformation import OpenAIChatCompletionStreamingHandler
from litellm.llms.openai_like.chat.transformation import OpenAILikeChatConfig
from litellm.types.llms.openai import AllMessageValues
Expand All @@ -41,6 +47,7 @@
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj

REASONING_OPEN_TAG: Final = "<reasoning>"
_SUPPORTED_OPENAI_PARAMS_ADAPTER: Final = TypeAdapter(list[str])
REASONING_CLOSE_TAG: Final = "</reasoning>"

CHAT_COMPLETIONS_REFUSED_PARAMS_BY_FAMILY: Final = MappingProxyType(
Expand Down Expand Up @@ -200,24 +207,6 @@ def chunk_parser(self, chunk: dict) -> ModelResponseStream: # mutable-ok: BaseM
return parsed


def with_max_completion_tokens(params: Mapping[str, object]) -> Mapping[str, object]:
"""
Send the caller's ``max_tokens`` as ``max_completion_tokens``.

Every model on this surface accepts ``max_completion_tokens`` and the GPT-5.6 family
rejects ``max_tokens``; an explicit ``max_completion_tokens`` wins when both are set.
"""
if "max_tokens" not in params:
return params
return MappingProxyType(
{
key: value
for key, value in (("max_completion_tokens", params["max_tokens"]), *params.items())
if key != "max_tokens"
}
)


class AmazonBedrockRuntimeChatCompletionsConfig(OpenAILikeChatConfig):
def __init__(self, aws_signer: BaseAWSLLM | None = None) -> None:
super().__init__()
Expand Down Expand Up @@ -394,15 +383,19 @@ def transform_response(
choice.message.content = content
return response

def get_supported_openai_params(self, model: str) -> list: # mutable-ok: BaseConfig signature
def get_supported_openai_params(self, model: str) -> list[str]: # mutable-ok: BaseConfig signature
refused: Final = frozenset(("n", *chat_completions_params_refused_for(model)))
base_params: Final = tuple(
param for param in super().get_supported_openai_params(model) if param not in refused
supported_params: Final = _SUPPORTED_OPENAI_PARAMS_ADAPTER.validate_python(
super().get_supported_openai_params(model) # pyright: ignore[reportUnknownMemberType] # base untyped
)
base_params: Final = tuple(param for param in supported_params if param not in refused)
reasoning_param: Final = (
("reasoning_effort",)
if "reasoning_effort" not in base_params
and litellm.supports_reasoning(model=model, custom_llm_provider=self.custom_llm_provider)
and (
bedrock_chat_completions_serves_tools_with_reasoning(model)
or litellm.supports_reasoning(model=model, custom_llm_provider=self.custom_llm_provider)
)
else ()
)
return [*base_params, *reasoning_param] # mutable-ok: BaseConfig signature returns a list
Expand Down
Loading
Loading