refactor: replace fresh getattr/setattr and test type-ignores with typed access

Same-day debt cleanup on code that landed in the last 24 hours. No behavior change.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Devin AI 2026-08-29 07:59:27 +00:00
parent ae7e50f096
commit 9bfb332904
8 changed files with 54 additions and 32 deletions

View file

@ -1,6 +1,6 @@
{
"reportAny": {
"limit": 18483
"limit": 18482
},
"reportArgumentType": {
"limit": 2557

View file

@ -8612,9 +8612,9 @@ def _joined_streamed_citations(streamed_citations: "tuple[object, ...]") -> "lis
def _stream_builder_model_map_cost(response: ModelResponse) -> float | None:
model_name: Final = getattr(response, "model", None)
model_name: Final = response.model
usage: Final = getattr(response, "usage", None)
if not isinstance(model_name, str) or not model_name or not isinstance(usage, Usage):
if not model_name or not isinstance(usage, Usage):
return None
try:
prompt_cost, completion_tokens_cost = litellm.cost_per_token(model=model_name, usage_object=usage)

View file

@ -301,12 +301,12 @@ class LakeraAIGuardrail(CustomGuardrail):
explicit sync below a hot reload that changes mode would pass validation but
keep dispatching on the stale event_hook.
"""
new_event_hook: Final = getattr(litellm_params, "mode", None) or self.event_hook
prospective_payload: Final = getattr(litellm_params, "payload", None)
prospective_breakdown: Final = getattr(litellm_params, "breakdown", None)
new_event_hook: Final = litellm_params.mode or self.event_hook
prospective_payload: Final = litellm_params.payload
prospective_breakdown: Final = litellm_params.breakdown
self._validate_advisory_config(
on_flagged=getattr(litellm_params, "on_flagged", None) or self.on_flagged,
advisory_system_message=getattr(litellm_params, "advisory_system_message", None),
on_flagged=litellm_params.on_flagged or self.on_flagged,
advisory_system_message=litellm_params.advisory_system_message,
payload=self.payload if prospective_payload is None else prospective_payload,
breakdown=self.breakdown if prospective_breakdown is None else prospective_breakdown,
)

View file

@ -121,7 +121,7 @@ class QualifireGuardrail(CustomGuardrail):
the live instance untouched instead of raising after it's already been
corrupted. Mirrors LakeraAIGuardrail's own override of this same method.
"""
prospective_on_flagged: Final = getattr(litellm_params, "on_flagged", None) or self.on_flagged
prospective_on_flagged: Final = litellm_params.on_flagged or self.on_flagged
self._validate_on_flagged(prospective_on_flagged)
super().update_in_memory_litellm_params(litellm_params=litellm_params)

View file

@ -413,14 +413,15 @@ class GuardrailRegistry:
raise Exception(f"Error getting guardrail from DB: {e}")
def _apply_configured_bool_override(instance: CustomGuardrail, litellm_params: LitellmParams, param_name: str) -> None:
"""Override ``instance.<param_name>`` only when ``litellm_params`` explicitly
sets it, preserving whatever default the guardrail's own constructor chose
def _apply_configured_bool_overrides(instance: CustomGuardrail, litellm_params: LitellmParams) -> None:
"""Override the parallel/raw-scan flags only when ``litellm_params`` explicitly
sets them, preserving whatever default the guardrail's own constructor chose
otherwise (its constructor default may be True, so blindly copying an
absent/None config value would silently clobber it back to False)."""
configured: Final = getattr(litellm_params, param_name, None)
if configured is not None:
setattr(instance, param_name, bool(configured))
if litellm_params.run_in_parallel is not None:
instance.run_in_parallel = bool(litellm_params.run_in_parallel)
if litellm_params.scan_raw_request is not None:
instance.scan_raw_request = bool(litellm_params.scan_raw_request)
class InMemoryGuardrailHandler:
@ -544,8 +545,7 @@ class InMemoryGuardrailHandler:
"skip_tool_message_in_guardrail are enabled together, which excludes every message from "
"scanning, so no request content would ever be scanned. Remove one of the two."
)
for override_param in ("run_in_parallel", "scan_raw_request"):
_apply_configured_bool_override(custom_guardrail_callback, litellm_params, override_param)
_apply_configured_bool_overrides(custom_guardrail_callback, litellm_params)
parsed_guardrail: Final = Guardrail(
guardrail_id=guardrail.get("guardrail_id"),
@ -803,7 +803,6 @@ class InMemoryGuardrailHandler:
previous_guardrail: Final = self.IN_MEMORY_GUARDRAILS.get(guardrail_id)
previous_source: Final = self._sources.get(guardrail_id, source)
# Remove from memory if exists (also removes from callbacks)
if guardrail_id in self.IN_MEMORY_GUARDRAILS:
self.delete_in_memory_guardrail(guardrail_id)

View file

@ -185,7 +185,7 @@ class PipelineExecutor:
# snapshot instead of `data` (which earlier pass_data steps in
# this same pipeline may have already rewritten), same reason
# the normal sequential/parallel guardrail loops do this.
scans_raw_request: Final = getattr(callback, "scan_raw_request", False)
scans_raw_request: Final = callback.scan_raw_request
hook_input: Final[dict] = ( # mutable-ok: same request-payload shape as data
independent_snapshot(raw_request_snapshot)
if scans_raw_request and raw_request_snapshot is not None

View file

@ -1416,7 +1416,7 @@ class ProxyLogging:
mutation is discarded and a warning is logged so the misconfiguration
is visible instead of silently forwarding unredacted content.
"""
scans_raw_request: Final = getattr(callback, "scan_raw_request", False)
scans_raw_request: Final = callback.scan_raw_request
should_use_raw_snapshot: Final = scans_raw_request and raw_request_snapshot is not None
input_data: Final = ( # mutable-ok: same request-payload shape as data
independent_snapshot(raw_request_snapshot) if should_use_raw_snapshot else data
@ -1453,7 +1453,7 @@ class ProxyLogging:
"scan_raw_request is for block-only guardrails and this mutation is being "
"discarded. Remove scan_raw_request from this guardrail's config if it needs "
"to mask/rewrite content.",
getattr(callback, "guardrail_name", None) or callback.__class__.__name__,
callback.guardrail_name or callback.__class__.__name__,
)
if scans_raw_request:
if result is not None:
@ -1778,7 +1778,7 @@ class ProxyLogging:
# guarantee must hold even under litellm.safe_memory_mode, which
# otherwise makes deep copies return the original object.
needs_raw_request_snapshot: Final = any(
isinstance(cb, CustomGuardrail) and getattr(cb, "scan_raw_request", False)
isinstance(cb, CustomGuardrail) and cb.scan_raw_request
for cb in ProxyLogging._callback_capabilities().resolved_callbacks
)
raw_request_snapshot: Final[dict | None] = ( # mutable-ok: same request-payload shape as data
@ -1938,7 +1938,7 @@ class ProxyLogging:
"""
def _input_for(callback: CustomGuardrail) -> dict: # mutable-ok: same request-payload shape as data
if not getattr(callback, "scan_raw_request", False) or raw_request_snapshot is None:
if not callback.scan_raw_request or raw_request_snapshot is None:
return data
return independent_snapshot(raw_request_snapshot)
@ -1962,11 +1962,7 @@ class ProxyLogging:
# deployment-level guardrail sharing this name would see no marker
# via _pre_call_hook_already_ran and re-run it a second time on
# live kwargs.
if (
getattr(callback, "scan_raw_request", False)
and not isinstance(result, BaseException)
and result is not None
):
if callback.scan_raw_request and not isinstance(result, BaseException) and result is not None:
callback.mark_pre_call_hook_ran(data)
raised: Final = tuple(result for result in results if isinstance(result, BaseException))
blocking: Final = next((exc for exc in raised if not _exception_changes_request_flow(exc)), None)

View file

@ -9,11 +9,14 @@ import pytest
from fastapi import HTTPException
import litellm
from litellm.caching.caching import DualCache
from litellm.exceptions import RejectedRequestError
from litellm.integrations.custom_guardrail import CustomGuardrail
from litellm.integrations.custom_logger import CustomLogger
from litellm.proxy._types import UserAPIKeyAuth
from litellm.proxy.utils import ProxyLogging
from litellm.types.guardrails import GuardrailEventHooks
from litellm.types.utils import CallTypesLiteral
def _load(module: str, name: str):
@ -473,7 +476,13 @@ class _RedactingGuardrail(CustomGuardrail):
kwargs.setdefault("event_hook", GuardrailEventHooks.pre_call)
super().__init__(guardrail_name="redactor", **kwargs)
async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type): # type: ignore[override]
async def async_pre_call_hook(
self,
user_api_key_dict: UserAPIKeyAuth,
cache: DualCache,
data: dict,
call_type: CallTypesLiteral,
) -> dict | None:
for msg in data.get("messages", []):
if "SECRET" in msg.get("content", ""):
msg["content"] = msg["content"].replace("SECRET", "[REDACTED]")
@ -488,7 +497,13 @@ class _BlockOnSecretGuardrail(CustomGuardrail):
kwargs.setdefault("event_hook", GuardrailEventHooks.pre_call)
super().__init__(guardrail_name="blocker", **kwargs)
async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type): # type: ignore[override]
async def async_pre_call_hook(
self,
user_api_key_dict: UserAPIKeyAuth,
cache: DualCache,
data: dict,
call_type: CallTypesLiteral,
) -> dict | None:
if any("SECRET" in msg.get("content", "") for msg in data.get("messages", [])):
raise HTTPException(status_code=400, detail="blocked: SECRET detected")
return None
@ -560,7 +575,13 @@ async def test_scan_raw_request_guardrail_does_not_undo_later_masking(
separate marker (PII_TOKEN) that only the redactor reacts to."""
class _PiiRedactor(_RedactingGuardrail):
async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type): # type: ignore[override]
async def async_pre_call_hook(
self,
user_api_key_dict: UserAPIKeyAuth,
cache: DualCache,
data: dict,
call_type: CallTypesLiteral,
) -> dict | None:
for msg in data.get("messages", []):
if "PII_TOKEN" in msg.get("content", ""):
msg["content"] = msg["content"].replace("PII_TOKEN", "[REDACTED]")
@ -692,7 +713,13 @@ async def test_scan_raw_request_warns_when_guardrail_mutation_discarded(
super().__init__(**kwargs)
self.scan_raw_request = True
async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type): # type: ignore[override]
async def async_pre_call_hook(
self,
user_api_key_dict: UserAPIKeyAuth,
cache: DualCache,
data: dict,
call_type: CallTypesLiteral,
) -> dict | None:
for msg in data.get("messages", []):
msg["content"] = msg["content"].replace("SECRET", "[REDACTED]")
return data