mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
fix(proxy): validate Responses guardrail event names
This commit is contained in:
parent
dfe2b4fd87
commit
4fb052e786
2 changed files with 28 additions and 1 deletions
|
|
@ -251,6 +251,7 @@ _TOOL_SEARCH_CALL_TYPE: Final = "tool_search_call"
|
|||
_OUTPUT_TOOL_CALL_ITEM_TYPES: Final = _TOOL_CALL_ITEM_TYPES | {_TOOL_SEARCH_CALL_TYPE}
|
||||
_SCANNED_TOOL_SEARCH_NAME: Final = "tool_search"
|
||||
_TOOL_SEARCH_ARGUMENTS_ADAPTER: Final = TypeAdapter(dict[str, object])
|
||||
_STREAM_EVENT_TYPE_ADAPTER: Final = TypeAdapter(str)
|
||||
_TOOL_CALL_PAYLOAD_FIELDS: Final[Mapping[str, str]] = MappingProxyType(
|
||||
{"function_call": "arguments", "custom_tool_call": "input"}
|
||||
)
|
||||
|
|
@ -1796,7 +1797,7 @@ class OpenAIResponsesHandler(BaseTranslation):
|
|||
else self._standalone_block_events(exc)
|
||||
)
|
||||
return tuple(
|
||||
f"event: {event.type.value if isinstance(event.type, ResponsesAPIStreamEvents) else event.type}\n"
|
||||
f"event: {_STREAM_EVENT_TYPE_ADAPTER.validate_python(stream_item_field(event, 'type'))}\n"
|
||||
f"data: {event.model_dump_json(exclude_none=True, exclude_unset=True, serialize_as_any=True)}\n\n".encode()
|
||||
for event in events
|
||||
)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ from litellm.constants import RETURN_RAW_MODEL_NAME_METADATA_KEY
|
|||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.proxy.common_request_processing import create_response
|
||||
from litellm.proxy.common_utils.responses_stream_errors import ResponsesStreamErrorState
|
||||
from litellm.proxy.proxy_server import (
|
||||
_apply_streaming_chunk_hooks,
|
||||
_fast_serialize_simple_model_response_stream,
|
||||
|
|
@ -145,6 +146,31 @@ async def test_responses_stream_dispatches_named_events_without_a_chat_done_sent
|
|||
assert payload["type"] == event_type
|
||||
assert json.loads(frames[1].split("data: ", 1)[1])["delta"] == "hello"
|
||||
|
||||
@pytest.mark.parametrize("binary", [False, True])
|
||||
@pytest.mark.parametrize(
|
||||
("event_type", "event_line"),
|
||||
[
|
||||
(None, ""),
|
||||
("", ""),
|
||||
("response.delta\ninjected", ""),
|
||||
("response.delta\rinjected", ""),
|
||||
("response.delta\r\ninjected", ""),
|
||||
("response.vendor_delta", "event: response.vendor_delta\n"),
|
||||
],
|
||||
)
|
||||
def test_responses_event_names_preserve_payload_without_injecting_sse_lines(
|
||||
event_type: str | None, event_line: str, binary: bool
|
||||
) -> None:
|
||||
payload: Final = {"type": event_type, "sequence_number": 0}
|
||||
data_line: Final = f"data: {json.dumps(payload)}\n\n"
|
||||
frame: Final = data_line.encode("utf-8") if binary else data_line
|
||||
expected: Final = (event_line + data_line).encode("utf-8") if binary else event_line + data_line
|
||||
state: Final = ResponsesStreamErrorState()
|
||||
state.observe_chunk(payload)
|
||||
|
||||
assert state.mark_emitted(frame) == expected
|
||||
|
||||
|
||||
class _FakeStream:
|
||||
def __init__(self, chunks, hidden_params=None):
|
||||
self._chunks = chunks
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue