From a0b6cd5a67e17e37d957c3316c3150f1aa79667e Mon Sep 17 00:00:00 2001 From: basil-k-aji-dev <70605804+basil-k-aji-dev@users.noreply.github.com> Date: Fri, 2 Oct 2026 11:35:54 +0530 Subject: [PATCH] feat(presidio): type the guardrail-only params at the read site Declaring them on LitellmParams cleared the basedpyright budget but changed the proxy OpenAPI spec, which then wants _lazy_openapi_snapshot.json and the dashboard's schema.d.ts regenerated. Both are generated from app.openapi(), so producing them in an environment with different optional dependencies risks committing a spec with routes missing. Annotate the reads instead. getattr returning Any is what counted against the unknown-argument budget; a declared type on the local fixes that without touching the public schema. If these belong on LitellmParams, that is a tidier home for them and a separate change. --- litellm/proxy/guardrails/guardrail_initializers.py | 9 +++++++-- litellm/types/guardrails.py | 14 -------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_initializers.py b/litellm/proxy/guardrails/guardrail_initializers.py index 56bbd8ff103..413faa11961 100644 --- a/litellm/proxy/guardrails/guardrail_initializers.py +++ b/litellm/proxy/guardrails/guardrail_initializers.py @@ -137,6 +137,11 @@ def initialize_presidio(litellm_params: LitellmParams, guardrail: Guardrail) -> _OPTIONAL_PresidioPIIMasking, ) + # Read through getattr with a declared type: these two are guardrail-only + # params, so they are not fields on LitellmParams, and an unannotated + # getattr returns Any and counts against the unknown-argument budget. + stable_tokens: Final[bool | None] = getattr(litellm_params, "presidio_stable_tokens", None) + token_salt: Final[str | None] = getattr(litellm_params, "presidio_token_salt", None) explicit_filter_scope: Final = litellm_params.presidio_filter_scope filter_scope: Final = explicit_filter_scope or ("input" if _is_mcp_only_mode(litellm_params.mode) else "both") run_input: Final = filter_scope in ("input", "both") @@ -156,8 +161,8 @@ def initialize_presidio(litellm_params: LitellmParams, guardrail: Guardrail) -> presidio_anonymizer_api_base=litellm_params.presidio_anonymizer_api_base, presidio_language=litellm_params.presidio_language, presidio_entities_deny_list=litellm_params.presidio_entities_deny_list, - presidio_stable_tokens=litellm_params.presidio_stable_tokens, - presidio_token_salt=litellm_params.presidio_token_salt, + presidio_stable_tokens=stable_tokens, + presidio_token_salt=token_salt, apply_to_output=False, timeout=litellm_params.timeout, _callback_role="scan", diff --git a/litellm/types/guardrails.py b/litellm/types/guardrails.py index d2ec10b0ab5..46026c12d24 100644 --- a/litellm/types/guardrails.py +++ b/litellm/types/guardrails.py @@ -494,20 +494,6 @@ class PresidioConfigModel(PresidioPresidioConfigModelUserInterface): default=None, description="Path to a JSON file containing ad-hoc recognizers for Presidio", ) - presidio_stable_tokens: bool | None = Field( - default=None, - description=( - "Derive PII placeholders from the value instead of its order in the request, " - "so the same value maps to the same token across turns. Requires presidio_token_salt." - ), - ) - presidio_token_salt: str | None = Field( - default=None, - description=( - "os.environ/ reference holding the HMAC key behind stable tokens. " - "A literal is refused: guardrail params are logged in full at debug level." - ), - ) presidio_analyze_chunk_size_bytes: int | None = Field( default=None, description=(