This commit is contained in:
shadowmodder 2026-11-07 05:46:20 +00:00 • committed by GitHub
commit 352cf2d1da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 49 additions and 1 deletions

View file

@ -105,6 +105,7 @@ _CHAT_COMPLETION_FIELDS: Final = frozenset((*ModelResponse.model_fields, "usage"
_RESPONSES_API_ONLY_FIELDS: Final = frozenset((*Response.model_fields, *ResponsesAPIResponse.model_fields)) - frozenset(
ChatCompletion.model_fields
)
_RESPONSES_INSTRUCTIONS_MAX_LENGTH: Final = 1_048_576
def _bridged_prompt_cache_breakpoint(marker: object, drop_params: bool, target_trims_breakpoints: bool) -> object:
@ -1199,9 +1200,12 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
)
# Keep the system text in input instead of instructions when the Responses API needs it there.
# It rejects an empty input, and json_object requires the word "json" in input, ignoring instructions.
# It also caps instructions at 1,048,576 characters, counted here in UTF-8 bytes to stay on the safe side.
typed_optional_params: Final = cast("Mapping[str, object]", optional_params) # cast-ok: untyped dict
system_goes_in_input: Final = converted_instructions is not None and (
not converted_input_items or _payload_type(typed_optional_params.get("response_format")) == "json_object"
not converted_input_items
or _payload_type(typed_optional_params.get("response_format")) == "json_object"
or len(converted_instructions.encode("utf-8")) > _RESPONSES_INSTRUCTIONS_MAX_LENGTH
)
target_input_items: Final = (
_without_audio_input_parts(converted_input_items)

View file

@ -1901,6 +1901,50 @@ def test_transform_request_json_object_keeps_system_prompt_where_the_json_word_c
assert (result["input"], result.get("instructions")) == (expected_input, expected_instructions), result
def _system_input_item(system_prompt: str) -> dict:
return {
"type": "message",
"role": "system",
"content": [{"type": "input_text", "text": system_prompt}],
}
@pytest.mark.parametrize(
("system_prompt", "expected_input", "expected_instructions"),
[
("You are a helpful assistant.", [_PLAIN_USER_ITEM], "You are a helpful assistant."),
("a" * 1_048_576, [_PLAIN_USER_ITEM], "a" * 1_048_576),
("a" * 1_048_577, [_system_input_item("a" * 1_048_577), _PLAIN_USER_ITEM], None),
("é" * 524_289, [_system_input_item("é" * 524_289), _PLAIN_USER_ITEM], None),
],
ids=[
"short_system_stays_in_instructions",
"system_at_cap_stays_in_instructions",
"system_over_cap_moves_to_input",
"multibyte_system_over_cap_in_bytes_moves_to_input",
],
)
def test_transform_request_overlong_system_prompt_moves_to_input(system_prompt, expected_input, expected_instructions):
"""Overlong system prompts go in input, since OpenAI caps instructions at 1,048,576 (#50243).
The cap is measured in UTF-8 bytes, so a prompt under the cap in characters still moves
to input when its byte length is over it.
"""
result = LiteLLMResponsesTransformationHandler().transform_request(
model="openai/gpt-6.1-terra",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": "List three colors."},
],
optional_params={},
litellm_params={},
headers={},
litellm_logging_obj=Mock(),
)
assert (result["input"], result.get("instructions")) == (expected_input, expected_instructions), result
def test_transform_request_single_char_keys_not_matched():
"""Test that single-character keys are not incorrectly matched to 'metadata' or 'previous_response_id'