mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
test(guardrails): align logging_only_scope integration rows with abort-on-input-failure and encrypted params
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
443f9e3db4
commit
733c2fcfdc
2 changed files with 43 additions and 6 deletions
|
|
@ -31,6 +31,8 @@ from openai import AsyncOpenAI, OpenAI
|
|||
from openai.types.chat import ChatCompletionChunk
|
||||
from pydantic import JsonValue, TypeAdapter
|
||||
|
||||
from litellm.proxy.common_utils.callback_utils import CALLBACK_VAR_ENCRYPTED_PREFIX
|
||||
from litellm.proxy.guardrails.guardrail_registry import decrypt_guardrail_litellm_params
|
||||
from tests.integration.cost_calculation.cost_tracking_case import JsonResponse, SseResponse
|
||||
|
||||
JSON_OBJECT: Final = TypeAdapter(dict[str, JsonValue])
|
||||
|
|
@ -850,7 +852,7 @@ def _create_guardrail(candidate: Gateway, identity: str, params: Mapping[str, Js
|
|||
|
||||
|
||||
def _management_guardrail_rows(identity: str) -> tuple[dict[str, JsonValue], ...]:
|
||||
return tuple(
|
||||
rows: Final = tuple(
|
||||
object_value(row)
|
||||
for row in read_rows(
|
||||
"SELECT guardrail_id, guardrail_name, litellm_params, guardrail_info "
|
||||
|
|
@ -858,6 +860,32 @@ def _management_guardrail_rows(identity: str) -> tuple[dict[str, JsonValue], ...
|
|||
(identity,),
|
||||
)
|
||||
)
|
||||
return tuple({**row, "litellm_params": _decrypted_management_litellm_params(row["litellm_params"])} for row in rows)
|
||||
|
||||
|
||||
def _decrypted_management_litellm_params(stored_value: JsonValue) -> dict[str, JsonValue]:
|
||||
stored: Final = object_value(stored_value)
|
||||
salt_key: Final = os.environ.get("LITELLM_SALT_KEY", "sk-integration-salt")
|
||||
with pytest.MonkeyPatch.context() as monkeypatch:
|
||||
monkeypatch.setenv("LITELLM_SALT_KEY", salt_key)
|
||||
decrypted: Final = JSON_OBJECT.validate_python(decrypt_guardrail_litellm_params(stored))
|
||||
assert _decryption_only_changes_encrypted_values(stored, decrypted)
|
||||
return decrypted
|
||||
|
||||
|
||||
def _decryption_only_changes_encrypted_values(stored: JsonValue, decrypted: JsonValue) -> bool:
|
||||
if isinstance(stored, dict) and isinstance(decrypted, dict):
|
||||
return stored.keys() == decrypted.keys() and all(
|
||||
_decryption_only_changes_encrypted_values(value, decrypted[key]) for key, value in stored.items()
|
||||
)
|
||||
if isinstance(stored, list) and isinstance(decrypted, list):
|
||||
return len(stored) == len(decrypted) and all(
|
||||
_decryption_only_changes_encrypted_values(stored_value, decrypted_value)
|
||||
for stored_value, decrypted_value in zip(stored, decrypted)
|
||||
)
|
||||
if isinstance(stored, str) and stored.startswith(CALLBACK_VAR_ENCRYPTED_PREFIX):
|
||||
return isinstance(decrypted, str) and not decrypted.startswith(CALLBACK_VAR_ENCRYPTED_PREFIX)
|
||||
return stored == decrypted
|
||||
|
||||
|
||||
def _drain_upstream(upstream_url: str) -> tuple[dict[str, JsonValue], ...]:
|
||||
|
|
@ -865,8 +893,10 @@ def _drain_upstream(upstream_url: str) -> tuple[dict[str, JsonValue], ...]:
|
|||
response.raise_for_status()
|
||||
requests: Final = object_value(JSON_OBJECT.validate_python(response.json())).get("requests")
|
||||
assert isinstance(requests, list), response.text
|
||||
_record_upstream_request_count(len(requests))
|
||||
return tuple(object_value(request) for request in requests)
|
||||
observations: Final = tuple(object_value(request) for request in requests)
|
||||
forwarded_requests: Final = tuple(request for request in observations if request.get("method", "POST") != "GET")
|
||||
_record_upstream_request_count(len(forwarded_requests))
|
||||
return forwarded_requests
|
||||
|
||||
|
||||
def _json_contains_exact_string(value: JsonValue, expected: str) -> bool:
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ def test_F7_guardrail_ui_settings_classify_directional_scope_support(gateway: Ga
|
|||
"logging_only",
|
||||
"sideways",
|
||||
200,
|
||||
("request", "response"),
|
||||
("request",),
|
||||
"logging_only",
|
||||
id="G3-yaml-logging-invalid-literal",
|
||||
),
|
||||
|
|
@ -1337,7 +1337,14 @@ def test_H10_management_patch_null_scope_restores_both_directions(gateway: Gatew
|
|||
|
||||
def policy(request: Request) -> Reply:
|
||||
assert request.target == "/beta/litellm_basic_guardrail_api", request.target
|
||||
return Reply(body=b'{"action":"BLOCKED","blocked_reason":"synthetic reset-scope monitor"}')
|
||||
payload: Final = JSON_OBJECT.validate_json(request.body)
|
||||
direction: Final = _direction(payload)
|
||||
verdict: Final = (
|
||||
{"action": "NONE"}
|
||||
if direction == "request"
|
||||
else {"action": "BLOCKED", "blocked_reason": "synthetic reset-scope monitor"}
|
||||
)
|
||||
return Reply(body=json.dumps(verdict).encode())
|
||||
|
||||
try:
|
||||
with wire_server(policy) as guardrail:
|
||||
|
|
@ -1407,7 +1414,7 @@ def test_H10_management_patch_null_scope_restores_both_directions(gateway: Gatew
|
|||
assert tuple(
|
||||
(entry["guardrail_name"], entry["guardrail_mode"], entry["guardrail_status"]) for entry in entries
|
||||
) == (
|
||||
(identity, "logging_only", "guardrail_intervened"),
|
||||
(identity, "logging_only", "success"),
|
||||
(identity, "logging_only", "guardrail_intervened"),
|
||||
), entries
|
||||
finally:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue