mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
fix(responses): keep the system prompt in input for json_object on the chat to responses bridge (#49957)
The bridge moves leading system messages into the Responses instructions field. With response_format json_object, Azure requires the word json in input and ignores instructions, so a request that follows the documented rule 400s after translation. Send the system text as a system input item when json_object is requested, the same shape already used for system-only requests Resolves LIT-10426
This commit is contained in:
parent
1967b41368
commit
9703e01cd4
2 changed files with 55 additions and 8 deletions
|
|
@ -1180,11 +1180,12 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
|
|||
include_namespace=True,
|
||||
replay_openai_history=provider == "openai",
|
||||
)
|
||||
# OpenAI's Responses API rejects an empty input. For a system-only
|
||||
# request, carry the system message as a system-role input item instead
|
||||
# of instructions, mirroring how non-string system content is already
|
||||
# handled in convert_chat_completion_messages_to_responses_api.
|
||||
is_system_only_request: Final = not converted_input_items and converted_instructions is not None
|
||||
# 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.
|
||||
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"
|
||||
)
|
||||
target_input_items: Final = (
|
||||
_without_audio_input_parts(converted_input_items)
|
||||
if _drops_audio_input(model, litellm_params)
|
||||
|
|
@ -1196,12 +1197,13 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
|
|||
"type": "message",
|
||||
"role": "system",
|
||||
"content": [{"type": "input_text", "text": converted_instructions}],
|
||||
}
|
||||
},
|
||||
*target_input_items,
|
||||
]
|
||||
if is_system_only_request
|
||||
if system_goes_in_input
|
||||
else target_input_items
|
||||
)
|
||||
instructions: Final = None if is_system_only_request else converted_instructions
|
||||
instructions: Final = None if system_goes_in_input else converted_instructions
|
||||
|
||||
# Build responses API request using the reverse transformation logic
|
||||
responses_api_request: Final = ResponsesAPIOptionalRequestParams()
|
||||
|
|
|
|||
|
|
@ -1856,6 +1856,51 @@ def test_transform_request_system_only_message_maps_to_system_input_item():
|
|||
assert not result.get("instructions")
|
||||
|
||||
|
||||
_JSON_SYSTEM_PROMPT: Final = "You output JSON objects."
|
||||
_PLAIN_USER_ITEM: Final = {
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [{"type": "input_text", "text": "List three colors."}],
|
||||
}
|
||||
_COLORS_SCHEMA: Final = {
|
||||
"type": "json_schema",
|
||||
"json_schema": {"name": "colors", "schema": {"type": "object", "properties": {}}},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("response_format", "expected_input", "expected_instructions"),
|
||||
[
|
||||
(
|
||||
{"type": "json_object"},
|
||||
[
|
||||
{"type": "message", "role": "system", "content": [{"type": "input_text", "text": _JSON_SYSTEM_PROMPT}]},
|
||||
_PLAIN_USER_ITEM,
|
||||
],
|
||||
None,
|
||||
),
|
||||
(_COLORS_SCHEMA, [_PLAIN_USER_ITEM], _JSON_SYSTEM_PROMPT),
|
||||
],
|
||||
ids=["json_object_keeps_system_in_input", "json_schema_keeps_system_in_instructions"],
|
||||
)
|
||||
def test_transform_request_json_object_keeps_system_prompt_where_the_json_word_check_reads_it(
|
||||
response_format, expected_input, expected_instructions
|
||||
):
|
||||
result = LiteLLMResponsesTransformationHandler().transform_request(
|
||||
model="gpt-5.4-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": _JSON_SYSTEM_PROMPT},
|
||||
{"role": "user", "content": "List three colors."},
|
||||
],
|
||||
optional_params={"response_format": response_format},
|
||||
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'
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue