mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-31 06:56:59 +00:00
fix(responses): keep the caller's request stripped of cache_control as before when a marker is copied
This commit is contained in:
parent
a6a1336dd1
commit
6a0d30bcab
2 changed files with 71 additions and 12 deletions
|
|
@ -586,12 +586,8 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
|
|||
marker_safe_input: Final = self._input_with_wire_safe_prompt_cache_breakpoints(
|
||||
model=model, input=validated_input, litellm_params=litellm_params
|
||||
)
|
||||
marked_input: Final = (
|
||||
self._input_with_prompt_cache_breakpoints(marker_safe_input)
|
||||
if self._reads_prompt_cache_breakpoints(
|
||||
model=model, litellm_params=litellm_params, prompt_cache_options=prompt_cache_options
|
||||
)
|
||||
else marker_safe_input
|
||||
reads_breakpoints: Final = self._reads_prompt_cache_breakpoints(
|
||||
model=model, litellm_params=litellm_params, prompt_cache_options=prompt_cache_options
|
||||
)
|
||||
maps_anthropic_tool_search: Final = self._targets_openai_api(litellm_params) and any(
|
||||
is_anthropic_tool_search_chat_tool(tool) for tool in tools or ()
|
||||
|
|
@ -601,20 +597,43 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
|
|||
if maps_anthropic_tool_search
|
||||
else tools
|
||||
)
|
||||
replayable_input: Final = (
|
||||
input_without_unanswered_anthropic_tool_search_calls(marked_input)
|
||||
if maps_anthropic_tool_search
|
||||
else marked_input
|
||||
)
|
||||
stripped_input, stripped_tools = self.remove_cache_control_flag_from_input_and_tools(
|
||||
model=model, input=replayable_input, tools=mapped_tools
|
||||
model=model,
|
||||
input=self._replayable_input(marker_safe_input, reads_breakpoints, maps_anthropic_tool_search),
|
||||
tools=mapped_tools,
|
||||
)
|
||||
if marker_safe_input is not validated_input:
|
||||
self._strip_caller_input_as_if_unmarked(
|
||||
model=model,
|
||||
caller_input=validated_input,
|
||||
reads_breakpoints=reads_breakpoints,
|
||||
maps_anthropic_tool_search=maps_anthropic_tool_search,
|
||||
)
|
||||
object_schema_tools: Final = self._tools_with_object_parameters(model=model, tools=stripped_tools)
|
||||
sanitized_tools: Final = self._sanitized_tool_schemas_for_openai(
|
||||
model=model, tools=object_schema_tools, litellm_params=litellm_params
|
||||
)
|
||||
return self._drop_foreign_tool_call_item_ids(stripped_input), sanitized_tools
|
||||
|
||||
def _replayable_input(
|
||||
self, input: str | ResponseInputParam, reads_breakpoints: bool, maps_anthropic_tool_search: bool
|
||||
) -> str | ResponseInputParam:
|
||||
marked_input: Final = self._input_with_prompt_cache_breakpoints(input) if reads_breakpoints else input
|
||||
if not maps_anthropic_tool_search:
|
||||
return marked_input
|
||||
return input_without_unanswered_anthropic_tool_search_calls(marked_input)
|
||||
|
||||
def _strip_caller_input_as_if_unmarked(
|
||||
self,
|
||||
model: str,
|
||||
caller_input: str | ResponseInputParam,
|
||||
reads_breakpoints: bool,
|
||||
maps_anthropic_tool_search: bool,
|
||||
) -> None:
|
||||
self.remove_cache_control_flag_from_input_and_tools(
|
||||
model=model, input=self._replayable_input(caller_input, reads_breakpoints, maps_anthropic_tool_search)
|
||||
)
|
||||
|
||||
def _input_with_wire_safe_prompt_cache_breakpoints(
|
||||
self, model: str, input: str | ResponseInputParam, litellm_params: GenericLiteLLMParams
|
||||
) -> str | ResponseInputParam:
|
||||
|
|
|
|||
|
|
@ -4382,6 +4382,46 @@ def test_cache_control_still_marks_a_part_whose_own_marker_is_malformed_under_dr
|
|||
}
|
||||
|
||||
|
||||
def _caller_input_after_transform(model: str, marker: object) -> str:
|
||||
hint: Final = {"type": "ephemeral"}
|
||||
marked: Final = {} if marker is None else {"prompt_cache_breakpoint": marker}
|
||||
input: Final = [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "input_text", "text": "Say ok", "cache_control": hint, **marked},
|
||||
{"type": "input_text", "text": "Thanks", "cache_control": hint},
|
||||
],
|
||||
"cache_control": hint,
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [{"type": "output_text", "text": "ok", "cache_control": hint, **marked}],
|
||||
},
|
||||
]
|
||||
_CONFIGS[0].transform_responses_api_request(
|
||||
model=model,
|
||||
input=cast("ResponseInputParam", input), # cast-ok: plain dicts shaped like the SDK's input items
|
||||
response_api_optional_request_params={},
|
||||
litellm_params=GenericLiteLLMParams(drop_params=True),
|
||||
headers={},
|
||||
)
|
||||
return json.dumps(input)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", ("gpt-4.1", "gpt-6.1-sol"))
|
||||
@pytest.mark.parametrize("marker", (_VALID_MARKERS[0], "yes"), ids=("explicit", "malformed"))
|
||||
def test_a_marker_leaves_the_caller_request_that_fallbacks_and_logs_read_as_an_unmarked_one_would(
|
||||
monkeypatch: pytest.MonkeyPatch, model: str, marker: object
|
||||
) -> None:
|
||||
monkeypatch.setattr(litellm, "drop_params", False)
|
||||
marker_json: Final = f', "prompt_cache_breakpoint": {json.dumps(marker)}'
|
||||
unmarked: Final = _caller_input_after_transform(model, None)
|
||||
assert _caller_input_after_transform(model, marker).replace(marker_json, "") == unmarked
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.usefixtures(httpx_transport.__name__)
|
||||
async def test_aresponses_forwards_previous_response_id_to_openai() -> None:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue