diff --git a/litellm/responses/main.py b/litellm/responses/main.py index deae6643300..e8472531ea7 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -100,7 +100,6 @@ from litellm.types.utils import ( all_litellm_params, ) from litellm.utils import ( - LITELLM_ONLY_PARAMS, ProviderConfigManager, client, ) @@ -644,12 +643,20 @@ def _chat_completion_request_fields(allowed_openai_params: Sequence[str] | None) *DEFAULT_CHAT_COMPLETION_PARAM_VALUES, *all_litellm_params, *GenericLiteLLMParams.model_fields, - *LITELLM_ONLY_PARAMS, *(allowed_openai_params or ()), ) ).difference(_RESPONSES_ONLY_REQUEST_FIELDS_NEVER_BRIDGED) +def _message_cache_control_points(kwargs: Mapping[str, object]) -> Mapping[str, object]: + points: Final = tuple( + point + for point in configured_injection_points(kwargs.get("cache_control_injection_points")) + if point.get("location") == "message" + ) + return {"cache_control_injection_points": points} if points else {} + + _ADDITIONAL_DROP_PARAMS: Final = TypeAdapter(list[str]) @@ -667,7 +674,12 @@ def _bridge_kwargs( ) -> Mapping[str, object] | _UntranslatableResponsesFields: forwarded_keys: Final = _chat_completion_request_fields(allowed_openai_params) if responses_api_provider_config is not None: - return MappingProxyType({key: value for key, value in kwargs.items() if key in forwarded_keys}) + return MappingProxyType( + { + **{key: value for key, value in kwargs.items() if key in forwarded_keys}, + **_message_cache_control_points(kwargs), + } + ) candidate_keys: Final = _RESPONSES_REQUEST_FIELDS.difference(forwarded_keys, _explicitly_dropped_fields(kwargs)) untranslatable: Final = tuple(sorted(key for key in candidate_keys if kwargs.get(key) is not None)) if untranslatable and not drops_unsupported_params(kwargs.get("drop_params")): diff --git a/tests/unit/responses/test_responses_api_bridge_flag.py b/tests/unit/responses/test_responses_api_bridge_flag.py index 249cffc97e2..4c4e849d6c4 100644 --- a/tests/unit/responses/test_responses_api_bridge_flag.py +++ b/tests/unit/responses/test_responses_api_bridge_flag.py @@ -412,17 +412,18 @@ class TestUseResponsesApiBridgeFlag: {"type": "function_call_output", "call_id": "call_a", "output": "contents of a"}, ], use_chat_completions_api=True, - cache_control_injection_points=[{"location": "message", "index": -1}], + cache_control_injection_points=[{"location": "tool_config"}, {"location": "message", "index": -1}], api_key="fake-provider-api-key", num_retries=0, ) - sent_messages: Final = json.loads(upstream.calls[0].request.read())["messages"] + sent_body: Final = json.loads(upstream.calls[0].request.read()) assert [ message.get("tool_call_id") or message["role"] - for message in sent_messages + for message in sent_body["messages"] if "cache_control" in json.dumps(message) ] == ["call_a"] + assert "cache_control_injection_points" not in sent_body def test_bridge_keeps_deployment_credentials_while_dropping_unknown_params(self, respx_mock: respx.MockRouter): upstream: Final = respx_mock.post( diff --git a/tests/unit/responses/test_responses_api_request_body.py b/tests/unit/responses/test_responses_api_request_body.py index 47f8d37e667..94cb0e87ba8 100644 --- a/tests/unit/responses/test_responses_api_request_body.py +++ b/tests/unit/responses/test_responses_api_request_body.py @@ -23,6 +23,7 @@ from pydantic import TypeAdapter import litellm from litellm._logging import verbose_logger +from litellm.integrations.anthropic_cache_control_hook import MAX_CACHE_CONTROL_BLOCKS from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.types.llms.openai import ResponsesAPIResponse from tests.unit.llms.bedrock.slow_upstream import ( @@ -1208,11 +1209,11 @@ async def test_the_ordinal_point_listed_first_takes_the_last_breakpoint_slot(ind tools=[ { "type": "function", - "name": name, + "name": f"t{i}", "parameters": {"type": "object", "properties": {}}, "cache_control": {"type": "ephemeral"}, } - for name in ("t0", "t1", "t2") + for i in range(MAX_CACHE_CONTROL_BLOCKS - 1) ], )