fix(responses): enable chat logprobs when include names message.output_text.logprobs

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
mateo 2026-11-02 19:54:32 +00:00
parent 94a46a07ce
commit 968600031a
3 changed files with 39 additions and 2 deletions

View file

@ -924,11 +924,22 @@ class ChatCompletionSession(TypedDict, total=False):
########### End of Initialize Classes used for Responses API ###########
OUTPUT_TEXT_LOGPROBS_INCLUDE: Final = "message.output_text.logprobs"
def _requested_top_logprobs(request_fields: Mapping[str, object]) -> int | None:
value: Final = request_fields.get("top_logprobs")
return value if isinstance(value, int) else None
def _requests_output_text_logprobs(request_fields: Mapping[str, object]) -> bool:
"""``top_logprobs`` or ``include: [message.output_text.logprobs]`` each ask the chat route for logprobs."""
include: Final = request_fields.get("include")
return _requested_top_logprobs(request_fields) is not None or (
isinstance(include, (list, tuple)) and OUTPUT_TEXT_LOGPROBS_INCLUDE in include
)
def _chain_session_metadata(chain_session: str, request_kwargs: Mapping[str, object]) -> Mapping[str, object]:
key: Final = "litellm_metadata" if request_kwargs.get("litellm_metadata") else "metadata"
metadata: Final = request_kwargs.get(key)
@ -1299,7 +1310,7 @@ class LiteLLMCompletionResponsesConfig:
"tools": tools,
"top_p": responses_api_request.get("top_p"),
"top_logprobs": top_logprobs,
"logprobs": True if top_logprobs is not None else None,
"logprobs": True if _requests_output_text_logprobs(responses_api_request) else None,
"user": responses_api_request.get("user"),
"temperature": responses_api_request.get("temperature"),
"parallel_tool_calls": responses_api_request.get("parallel_tool_calls"),

View file

@ -108,6 +108,21 @@ GEMINI_2_5_FLASH_TOP_LOGPROBS_TEST_CASE: Final = replace(
)
@pytest.mark.parametrize("case", [GEMINI_2_5_FLASH_TOP_LOGPROBS_TEST_CASE], ids=lambda case: case.id)
GEMINI_2_5_FLASH_INCLUDE_LOGPROBS_TEST_CASE: Final = replace(
GEMINI_2_5_FLASH_TOP_LOGPROBS_TEST_CASE,
scenario="include_output_text_logprobs",
litellm_request={**GEMINI_2_5_FLASH_TEST_CASE.litellm_request, "include": ["message.output_text.logprobs"]},
expected_provider_request={
**GEMINI_2_5_FLASH_TEST_CASE.expected_provider_request,
"generationConfig": {**GENERATION_CONFIG, "responseLogprobs": True},
},
)
@pytest.mark.parametrize(
"case",
[GEMINI_2_5_FLASH_TOP_LOGPROBS_TEST_CASE, GEMINI_2_5_FLASH_INCLUDE_LOGPROBS_TEST_CASE],
ids=lambda case: case.id,
)
def test_responses_sampling_vertex_ai(case: TranslationTestCase, gateway: Gateway, provider: SharedProvider) -> None:
assert_translation(case, gateway, provider)

View file

@ -7489,6 +7489,17 @@ def test_responses_top_logprobs_bridges_to_chat_logprobs() -> None:
assert (request["top_logprobs"], request["logprobs"]) == (2, True)
def test_responses_include_output_text_logprobs_bridges_to_chat_logprobs() -> None:
request: Final = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
model="gemini-3.5-flash",
input="Say hello.",
responses_api_request={"include": ["message.output_text.logprobs"]},
custom_llm_provider="vertex_ai",
)
assert request["logprobs"] is True and "top_logprobs" not in request
def test_responses_without_top_logprobs_bridges_without_chat_logprobs() -> None:
request: Final = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
model="gemini-3.5-flash", input="Say hello.", responses_api_request={}, custom_llm_provider="vertex_ai"