fix(responses): validate replay fields when action is missing

This commit is contained in:
moe-berri 2026-10-27 11:31:32 -07:00
parent 552cb9fde7
commit 1c85934231
2 changed files with 43 additions and 6 deletions

View file

@ -2062,11 +2062,19 @@ class LiteLLMCompletionResponsesConfig:
- ItemReference
"""
if input_item.get("type") == "web_search_call":
search: Final = (
ResponseFunctionWebSearch.model_validate(input_item)
if "action" in input_item
else ResponseFunctionWebSearch.model_construct(None, **input_item)
)
try:
search: Final = ResponseFunctionWebSearch.model_validate(input_item)
except ValidationError as exc:
errors: Final = exc.errors(include_url=False, include_input=False)
if len(errors) != 1 or errors[0]["type"] != "missing" or errors[0]["loc"] != ("action",):
raise
replay: Final = ResponseFunctionWebSearch.model_construct(None, **input_item)
return [
GenericChatCompletionMessage(
role="assistant",
content="Hosted web search: " + replay.model_dump_json(exclude_none=True),
)
]
return [
GenericChatCompletionMessage(
role="assistant",

View file

@ -14,7 +14,7 @@ from openai.types.responses.response_function_web_search import (
ActionSearchSource,
ResponseFunctionWebSearch,
)
from pydantic import TypeAdapter
from pydantic import TypeAdapter, ValidationError
import litellm
from litellm.constants import CALLER_SESSION_ID_METADATA_KEY
@ -5077,6 +5077,35 @@ class TestHostedWebSearchReplay:
assert isinstance(content, str)
assert json.loads(content[content.index("{") :]) == input_item
@pytest.mark.parametrize(
"fields",
(
{"status": "completed"},
{"id": "ws_invalid"},
{"id": 42, "status": "completed"},
{"id": "ws_invalid", "status": "invalid"},
{"id": "ws_invalid", "status": "completed", "action": None},
{"id": "ws_invalid", "status": "completed", "action": 42},
{"id": "ws_invalid", "status": "completed", "action": {"type": "invalid"}},
{"id": "ws_invalid", "status": "completed", "action": {"type": "find_in_page"}},
),
ids=(
"missing-id",
"missing-status",
"invalid-id",
"invalid-status",
"null-action",
"invalid-action-type",
"unknown-action",
"incomplete-action",
),
)
def test_rejects_search_with_other_invalid_fields(self, fields: Mapping[str, object]) -> None:
with pytest.raises(ValidationError):
LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages(
input=[{"type": "web_search_call", **fields}], responses_api_request={}
)
@pytest.mark.parametrize("order", ((0, 1, 2, 3), (1, 0, 3, 2), (1, 3, 0, 2)))
@pytest.mark.parametrize("modify_params", (False, True))
@pytest.mark.parametrize("structured_content", (False, True))