Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
84084d9
feat(bedrock): send grok chat completions through runtime openai path
mateo-berri Sep 11, 2026
df7e9da
Merge remote-tracking branch 'origin/main' into litellm_bedrock_grok_…
mateo-berri Sep 19, 2026
6b9f067
feat(bedrock): serve gpt-oss and gpt-5.6 chat completions on runtime'…
mateo-berri Sep 20, 2026
a1c089f
fix(bedrock): route gpt-oss response_format to Converse and decide th…
mateo-berri Sep 20, 2026
b90c2f1
fix(bedrock): serve region-path and GovCloud gpt-oss ids on native Ch…
mateo-berri Sep 20, 2026
22b217b
fix(bedrock): keep params AWS refuses natively off the chat completio…
mateo-berri Sep 21, 2026
0a85e27
fix(bedrock): keep schema-less json_object on Converse for the chat c…
mateo-berri Sep 21, 2026
0139dd0
fix(bedrock): keep every json_object response_format on Converse for …
mateo-berri Sep 21, 2026
36759ee
Merge origin/main into litellm_bedrock_grok_chat_completions
mateo-berri Sep 24, 2026
c4c24dd
fix(rust): declare the bedrock runtime chat completions flags on Mode…
mateo-berri Sep 24, 2026
ff8e15d
fix(bedrock): opt into the native chat completions route through supp…
mateo-berri Sep 24, 2026
4101c0c
docs(cost-map): describe the bedrock native chat completions capabili…
mateo-berri Sep 25, 2026
61a130c
revert: docs(cost-map): describe the bedrock native chat completions …
mateo-berri Sep 25, 2026
e706095
merge: main into litellm_bedrock_grok_chat_completions
mateo-berri Sep 26, 2026
c60249f
test(bedrock): move the native chat completions tests under tests/unit
mateo-berri Sep 26, 2026
f4d84f8
fix(bedrock): drop reasoning_effort none for grok on the native chat …
mateo-berri Sep 26, 2026
e653217
fix(bedrock): keep converse extension params on the converse route
mateo-berri Sep 26, 2026
093a9d4
fix(bedrock): inline http image urls and keep stop on converse for na…
mateo-berri Sep 26, 2026
daba257
refactor(bedrock): share the sync remote media inliner
mateo-berri Sep 26, 2026
cbf01c2
fix(image-handling): infer the image mime type when the server sends …
mateo-berri Sep 26, 2026
4900a1a
fix(bedrock): stop sending aws_bedrock_project_id as OpenAI-Project o…
mateo-berri Sep 26, 2026
ef03d19
Merge remote-tracking branch 'origin/main' into litellm_bedrock_grok_…
mateo-berri Sep 28, 2026
3a390e6
feat(bedrock): make native chat completions an opt-in bedrock/chat_co…
mateo-berri Sep 29, 2026
84ae459
fix(bedrock): keep chat_completions/-prefixed deployments on the nati…
mateo-berri Sep 29, 2026
77204f8
fix(bedrock): keep provider response headers on the runtime chat comp…
mateo-berri Sep 29, 2026
615eff5
Merge remote-tracking branch 'origin/main' into litellm_bedrock_grok_…
mateo-berri Sep 30, 2026
3e3b9cd
feat(bedrock): serve gpt-5.6 and newer on runtime chat completions by…
mateo-berri Sep 30, 2026
22e35c6
fix(bedrock): refuse temperature and top_p natively on GPT 5.6 and ne…
mateo-berri Sep 30, 2026
952adfc
fix(bedrock): refuse GPT sampling and logprob params natively only wh…
mateo-berri Oct 1, 2026
19176f6
refactor(bedrock): keep the Converse route-prefix strip inside the be…
mateo-berri Oct 1, 2026
a0cef91
fix(bedrock): forward a non-string reasoning_effort on the native rou…
mateo-berri Oct 1, 2026
708f5a5
chore: merge main into litellm_bedrock_grok_chat_completions
mateo-berri Oct 1, 2026
dae29c1
fix(bedrock): route overlong GPT version digits to Converse and send …
mateo-berri Oct 1, 2026
578f26e
fix(bedrock): route model_id overrides to Converse and never send an …
mateo-berri Oct 1, 2026
4ee7ff4
test(bedrock): audit the native GPT chat completions route on the int…
mateo-berri Oct 1, 2026
2f27eb3
test(bedrock): harden the runtime chat completions audit cells
mateo-berri Oct 2, 2026
6daf0b7
fix(bedrock): refuse or drop a non-string reasoning_effort before the…
mateo-berri Oct 2, 2026
e518e0e
test(bedrock): pin router retries off and give the chaos bursts confi…
mateo-berri Oct 2, 2026
a109787
Merge remote-tracking branch 'origin/main' into litellm_bedrock_grok_…
mateo-berri Oct 2, 2026
6befa50
test(bedrock): wait for the replacement worker before tearing down th…
mateo-berri Oct 3, 2026
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
3 changes: 3 additions & 0 deletions litellm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,9 @@ def set_global_gitlab_config(config: Dict[str, Any]) -> None:
from .llms.bedrock.chat.invoke_transformations.amazon_openai_transformation import (
AmazonBedrockOpenAIConfig as AmazonBedrockOpenAIConfig,
)
from .llms.bedrock.chat.chat_completions.transformation import (
AmazonBedrockRuntimeChatCompletionsConfig as AmazonBedrockRuntimeChatCompletionsConfig,
)
from .llms.bedrock.image_generation.amazon_stability1_transformation import (
AmazonStabilityConfig as AmazonStabilityConfig,
)
Expand Down
5 changes: 5 additions & 0 deletions litellm/_lazy_imports_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@
"AmazonTwelveLabsPegasusConfig",
"AmazonInvokeConfig",
"AmazonBedrockOpenAIConfig",
"AmazonBedrockRuntimeChatCompletionsConfig",
"AmazonStabilityConfig",
"AmazonStability3Config",
"AmazonNovaCanvasConfig",
Expand Down Expand Up @@ -853,6 +854,10 @@
".llms.bedrock.chat.invoke_transformations.amazon_openai_transformation",
"AmazonBedrockOpenAIConfig",
),
"AmazonBedrockRuntimeChatCompletionsConfig": (
".llms.bedrock.chat.chat_completions.transformation",
"AmazonBedrockRuntimeChatCompletionsConfig",
),
"AmazonStabilityConfig": (
".llms.bedrock.image_generation.amazon_stability1_transformation",
"AmazonStabilityConfig",
Expand Down
404 changes: 404 additions & 0 deletions litellm/llms/bedrock/chat/chat_completions/transformation.py

Large diffs are not rendered by default.

142 changes: 131 additions & 11 deletions litellm/llms/bedrock/common_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
)
from litellm.llms.base_llm.base_utils import BaseLLMModelInfo, BaseTokenCounter
from litellm.llms.base_llm.chat.transformation import BaseLLMException
from litellm.llms.bedrock.request_metadata import bedrock_request_metadata_is_owned
from litellm.secret_managers.main import get_secret, get_secret_str
from litellm.types.llms.bedrock import AWS_AUTH_PARAM_KEYS, AwsAuthParams

Expand All @@ -37,6 +38,18 @@

_ERROR_REQUEST_URL: Final = "https://docs.litellm.ai/docs"
_OPENAI_FAMILY_MODEL_RE: Final = re.compile(r"(^|[./])openai\.")
BedrockRoute = Literal[
"converse",
"invoke",
"claude_platform",
"converse_like",
"agent",
"agentcore",
"async_invoke",
"openai",
"mantle",
"chat_completions",
]


def error_response_text(response: httpx.Response) -> str:
Expand Down Expand Up @@ -782,6 +795,111 @@ def strip_bedrock_routing_prefix(model: str) -> str:
return model


def split_bedrock_region_path(model: str) -> tuple[str | None, str]:
"""Split a ``<region>/<model-id>`` routing path into the region and the id AWS receives.

``bedrock/us-gov-west-1/openai.gpt-oss-20b-1:0`` -> ``("us-gov-west-1", "openai.gpt-oss-20b-1:0")``;
a model without a region path comes back as ``(None, <routing-prefix-stripped id>)``.
"""
stripped: Final = strip_bedrock_routing_prefix(model)
region, separator, model_id = stripped.partition("/")
if separator and region in _get_all_bedrock_regions():
return region, model_id
return None, stripped


def _bedrock_price_map_flag(model: str, flag: str) -> bool:
entries: Final = (
litellm.model_cost.get(key)
for key in (model, strip_bedrock_routing_prefix(model), split_bedrock_region_path(model)[1])
)
return any(entry is not None and entry.get(flag) is True for entry in entries)


def uses_bedrock_runtime_chat_completions(model: str) -> bool:
"""Whether this Bedrock model should use runtime native Chat Completions.

Data-driven from the price-map ``supports_bedrock_runtime_chat_completions`` flag
so onboarding a model is a JSON change. Explicit ``converse/`` still wins in
``get_bedrock_route`` because prefix routes are checked first, and a request
that needs a Converse-only feature (``bedrock_request_needs_converse``) is
served by Converse even on a flagged model.
"""
return _bedrock_price_map_flag(model, "supports_bedrock_runtime_chat_completions")


def bedrock_runtime_chat_completions_serves_tools_with_reasoning(model: str) -> bool:
"""Whether AWS's native Chat Completions serves this model's function tools with any ``reasoning_effort``.

Data-driven from the price-map ``supports_bedrock_runtime_chat_completions_tools_with_reasoning``
flag (gpt-oss, Grok). Without it AWS only takes tools with ``reasoning_effort="none"``
(the GPT-5.6 family), and Converse serves tools with any effort, so those requests fall back to it.
"""
return _bedrock_price_map_flag(model, "supports_bedrock_runtime_chat_completions_tools_with_reasoning")


def bedrock_runtime_chat_completions_enforces_response_format(model: str) -> bool:
"""Whether AWS's native Chat Completions enforces a ``response_format`` schema for this model.

Data-driven from the price-map ``supports_bedrock_runtime_chat_completions_response_format`` flag
(GPT-5.6, Grok). Without it AWS accepts the field and answers with unconstrained text (gpt-oss), so
Converse, which emulates the schema through a forced ``json_tool_call`` tool, serves those requests.
"""
return _bedrock_price_map_flag(model, "supports_bedrock_runtime_chat_completions_response_format")


BEDROCK_CONVERSE_ONLY_REQUEST_KEYS: Final = frozenset(
("guardrailConfig", "performanceConfig", "serviceTier", "requestMetadata", "outputConfig", "thinking")
)


def _response_format_constrains_output(response_format: object) -> bool:
if response_format is None:
return False
return not (isinstance(response_format, Mapping) and response_format.get("type") == "text")


def bedrock_request_needs_converse(model: str, request_params: Mapping[str, object]) -> bool:
"""Whether a request on a runtime-Chat-Completions model must still be served by Converse.

Converse-shaped body keys (``BEDROCK_CONVERSE_ONLY_REQUEST_KEYS``, the Anthropic-style ``thinking``
block included, which only Converse forwards as ``additionalModelRequestFields``) have no field on
AWS's native OpenAI surface, operator-owned request metadata is only written onto the Converse body,
function tools (``tools`` or legacy ``functions``) on a model without
``supports_bedrock_runtime_chat_completions_tools_with_reasoning`` are rejected there unless
``reasoning_effort`` is exactly ``"none"``, and a constraining ``response_format`` on a model without
``supports_bedrock_runtime_chat_completions_response_format`` is only honored by Converse.
"""
if any(request_params.get(key) is not None for key in BEDROCK_CONVERSE_ONLY_REQUEST_KEYS):
return True
if bedrock_request_metadata_is_owned():
return True
if _response_format_constrains_output(
request_params.get("response_format")
) and not bedrock_runtime_chat_completions_enforces_response_format(model):
return True
if not (request_params.get("tools") or request_params.get("functions")):
return False
return (
not bedrock_runtime_chat_completions_serves_tools_with_reasoning(model)
and request_params.get("reasoning_effort") != "none"
)


def bedrock_route_for_request(
model: str, request_params: Mapping[str, object], additional_drop_params: Sequence[str] | None
) -> BedrockRoute:
"""The route for one request, decided from the caller's raw params before any provider mapping.

Param mapping and dispatch both call this with the same inputs, so a request that falls back to
Converse is mapped with the Converse config and sent to Converse, never one without the other.
"""
dropped: Final = frozenset(additional_drop_params or ())
return BedrockModelInfo.get_bedrock_route(
model, {key: value for key, value in request_params.items() if key not in dropped}
)


def strip_bedrock_throughput_suffix(model: str) -> str:
"""Strip throughput tier suffixes and context window suffixes from Bedrock model names."""
import re
Expand Down Expand Up @@ -1116,19 +1234,13 @@ def _supported_cross_region_inference_region() -> list[str]:
@staticmethod
def get_bedrock_route(
model: str,
) -> Literal[
"converse",
"invoke",
"claude_platform",
"converse_like",
"agent",
"agentcore",
"async_invoke",
"openai",
"mantle",
]:
request_params: Mapping[str, object] | None = None,
) -> BedrockRoute:
"""
Get the bedrock route for the given model.

``request_params`` (the caller's chat params) lets a runtime Chat Completions
model fall back to Converse for the requests only Converse can serve.
"""
route_mappings: dict[
str,
Expand All @@ -1142,6 +1254,7 @@ def get_bedrock_route(
"async_invoke",
"openai",
"mantle",
"chat_completions",
],
] = {
"invoke/": "invoke",
Expand Down Expand Up @@ -1171,6 +1284,11 @@ def get_bedrock_route(
if is_bedrock_application_inference_profile_arn(model):
return "converse"

if uses_bedrock_runtime_chat_completions(model) and not (
request_params is not None and bedrock_request_needs_converse(model, request_params)
):
return "chat_completions"

base_model: Final = BedrockModelInfo.get_base_model(model)
alt_model: Final = BedrockModelInfo.get_non_litellm_routing_model_name(model=model)
if base_model in litellm.bedrock_converse_models or alt_model in litellm.bedrock_converse_models:
Expand Down Expand Up @@ -1347,6 +1465,8 @@ def get_bedrock_chat_config(model: str):
return litellm.AmazonConverseConfig()
elif bedrock_route == "openai":
return litellm.AmazonBedrockOpenAIConfig()
elif bedrock_route == "chat_completions":
return litellm.AmazonBedrockRuntimeChatCompletionsConfig()
elif bedrock_route == "agent":
from litellm.llms.bedrock.chat.invoke_agent.transformation import (
AmazonInvokeAgentConfig,
Expand Down
7 changes: 5 additions & 2 deletions litellm/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
from litellm.llms.base_llm.base_model_iterator import (
convert_model_response_to_streaming,
)
from litellm.llms.bedrock.common_utils import BedrockModelInfo
from litellm.llms.bedrock.common_utils import BedrockModelInfo, bedrock_route_for_request
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
from litellm.llms.cohere.common_utils import CohereModelInfo
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler, http2_enabled
from litellm.llms.openai.chat.gpt_5_transformation import OpenAIGPT5Config
Expand Down Expand Up @@ -4078,7 +4078,9 @@
if "aws_region_name" not in optional_params or optional_params["aws_region_name"] is None:
optional_params["aws_region_name"] = aws_bedrock_client.meta.region_name

bedrock_route: Final = BedrockModelInfo.get_bedrock_route(model)
bedrock_route: Final = bedrock_route_for_request(
model, ctx.request_params, ctx.kwargs.get("additional_drop_params")
)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved
if bedrock_route == "claude_platform":
provider_config = ProviderConfigManager.get_provider_chat_config(
model=model,
Expand Down Expand Up @@ -5686,6 +5688,7 @@
optional_params=optional_params,
organization=organization,
provider_config=provider_config,
request_params={**optional_param_args, **non_default_params},
shared_session=shared_session,
stream=stream,
temperature=temperature,
Expand Down
Loading
Loading