mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-06 07:48:06 +00:00
fix(responses): ask no team permission for string metadata named like a guardrail pick on socket turns
This commit is contained in:
parent
119c67f655
commit
18d96358d6
2 changed files with 29 additions and 3 deletions
|
|
@ -2331,10 +2331,17 @@ async def _check_frame_may_modify_guardrails(
|
|||
team_object: Callable[[UserAPIKeyAuth], Awaitable[LiteLLM_TeamTable | None]],
|
||||
) -> None:
|
||||
"""A frame that picks guardrails or policies needs the team's permission to modify guardrails"""
|
||||
if any("policies" in source or requests_guardrail_modification(source) for source in sources):
|
||||
if any(
|
||||
"policies" in source or requests_guardrail_modification(_without_client_metadata(source)) for source in sources
|
||||
):
|
||||
require_guardrail_modification_permission(await team_object(user_api_key_dict))
|
||||
|
||||
|
||||
def _without_client_metadata(source: Mapping[str, object]) -> Mapping[str, object]:
|
||||
metadata: Final = source.get("metadata")
|
||||
return {**source, "metadata": _metadata_picks("metadata", metadata)} if isinstance(metadata, dict) else source
|
||||
|
||||
|
||||
def _forwarded_frame_fields(source: Mapping[str, object]) -> dict[str, object]:
|
||||
return {
|
||||
key: _without_guardrail_selection(value) if key == "metadata" else value
|
||||
|
|
@ -2422,7 +2429,11 @@ def _metadata_picks(key: str, metadata: object) -> Mapping[str, object]:
|
|||
validated: Final = _WS_FRAME.validate_python(metadata)
|
||||
if key != "metadata":
|
||||
return validated
|
||||
return {name: item for name, item in validated.items() if not isinstance(item, str)}
|
||||
return {
|
||||
name: item
|
||||
for name, item in validated.items()
|
||||
if name not in _METADATA_GUARDRAIL_SELECTORS or not isinstance(item, str)
|
||||
}
|
||||
|
||||
|
||||
def _frame_guardrail_selection(
|
||||
|
|
|
|||
|
|
@ -2577,18 +2577,23 @@ class TestResponsesWSFirstFrameModelAuth:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_frame_guard_forwards_string_metadata_named_like_a_pick_without_running_it(self):
|
||||
from litellm.proxy._types import LiteLLM_TeamTable
|
||||
from litellm.proxy.response_api_endpoints.endpoints import _responses_ws_frame_guard
|
||||
|
||||
key_required = _FrameGuardrail("key-required")
|
||||
frame_pick = _FrameGuardrail("frame-pick")
|
||||
metadata = {"guardrails": "frame-pick", "guardrail_config": "strict", "trace": "t1"}
|
||||
|
||||
async def locked_team(user_api_key_dict: UserAPIKeyAuth) -> LiteLLM_TeamTable:
|
||||
return LiteLLM_TeamTable(team_id="team-1", metadata={"guardrails": {"modify_guardrails": False}})
|
||||
|
||||
with _guardrails_on(key_required, frame_pick) as proxy_logging_obj:
|
||||
guard = _responses_ws_frame_guard(
|
||||
{"model": "gpt-4o-mini", "litellm_metadata": {"guardrails": ["key-required"]}},
|
||||
_key(),
|
||||
proxy_logging_obj,
|
||||
MagicMock(),
|
||||
team_object=locked_team,
|
||||
)
|
||||
guarded = await guard(json.dumps({"type": "response.create", "input": "hi", "metadata": metadata}))
|
||||
|
||||
|
|
@ -2983,12 +2988,22 @@ class TestResponsesWSFirstFrameModelAuth:
|
|||
{"type": "response.create", "input": "hi", "guardrails": ["frame-pick"]},
|
||||
{"type": "response.create", "input": "hi", "metadata": {"guardrails": ["frame-pick"]}},
|
||||
{"type": "response.create", "input": "hi", "metadata": {"disable_global_guardrails": True}},
|
||||
{"type": "response.create", "input": "hi", "metadata": {"disable_global_guardrails": "true"}},
|
||||
{"type": "response.create", "response": {"input": "hi", "guardrails": ["frame-pick"]}},
|
||||
{"type": "response.create", "litellm_metadata": {"guardrails": ["frame-pick"]}, "response": {"input": "hi"}},
|
||||
{"type": "response.create", "input": "hi", "policies": ["frame-policy"]},
|
||||
{"type": "response.create", "response": {"input": "hi", "policies": ["frame-policy"]}},
|
||||
],
|
||||
ids=["top-level", "metadata", "global-toggle", "nested", "beside-nested", "policies", "nested-policies"],
|
||||
ids=[
|
||||
"top-level",
|
||||
"metadata",
|
||||
"global-toggle",
|
||||
"string-global-toggle",
|
||||
"nested",
|
||||
"beside-nested",
|
||||
"policies",
|
||||
"nested-policies",
|
||||
],
|
||||
)
|
||||
async def test_frame_guard_refuses_guardrail_and_policy_picks_from_a_team_without_permission(self, frame: dict[str, object]):
|
||||
from litellm.proxy._types import LiteLLM_TeamTable
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue