fix(guardrails): Presidio usa en_core_web_sm cacheado (en_core_web_lg no está en la imagen)
`detect_pii` instanciaba `AnalyzerEngine()` sin configuración, así que Presidio
intentaba cargar su modelo por defecto `en_core_web_lg` (~560 MB), que no está en
la imagen Docker — `core/Dockerfile` instala `en_core_web_sm`. Resultado: el
validador fallaba con `[E050] Can't find model 'en_core_web_lg'` y, al ser la
política fail-closed, *toda* invocación quedaba `blocked_by_guardrail`. Además se
reinstanciaba el engine en cada llamada (caro; probablemente la causa del crash del
proceso al segundo invoke). Ahora:
- `_presidio_analyzer()`: singleton perezoso con `NlpEngineProvider` → `en_core_web_sm`.
- si Presidio no está instalado o el modelo no carga, se hace fallback a la
detección por regex (antes solo se hacía fallback ante ImportError).
Detectado ejecutando el smoke de docker-compose (el venv local no tiene Presidio,
así que los tests usan la rama de regex y no lo veían).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,33 @@ _INJECTION_PATTERNS = [
|
|||||||
r"reveal (the )?(system|hidden) (prompt|instruction)",
|
r"reveal (the )?(system|hidden) (prompt|instruction)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Singleton perezoso del AnalyzerEngine de Presidio. Construirlo es caro (carga el
|
||||||
|
# modelo spaCy y los recognizers), así que se reutiliza entre llamadas.
|
||||||
|
_PRESIDIO_ANALYZER: Any = None
|
||||||
|
|
||||||
|
|
||||||
|
def _presidio_analyzer() -> Any:
|
||||||
|
"""``AnalyzerEngine`` de Presidio configurado con el modelo spaCy ``en_core_web_sm``.
|
||||||
|
|
||||||
|
Presidio usa por defecto ``en_core_web_lg`` (~560 MB), que **no** está en la imagen
|
||||||
|
Docker: ``core/Dockerfile`` instala ``en_core_web_sm``. Si Presidio no está instalado
|
||||||
|
o el modelo no se puede cargar, esto lanza y el llamador (`detect_pii`) hace fallback
|
||||||
|
a la detección por regex.
|
||||||
|
"""
|
||||||
|
global _PRESIDIO_ANALYZER
|
||||||
|
if _PRESIDIO_ANALYZER is None:
|
||||||
|
from presidio_analyzer import AnalyzerEngine
|
||||||
|
from presidio_analyzer.nlp_engine import NlpEngineProvider
|
||||||
|
|
||||||
|
nlp_engine = NlpEngineProvider(
|
||||||
|
nlp_configuration={
|
||||||
|
"nlp_engine_name": "spacy",
|
||||||
|
"models": [{"lang_code": "en", "model_name": "en_core_web_sm"}],
|
||||||
|
}
|
||||||
|
).create_engine()
|
||||||
|
_PRESIDIO_ANALYZER = AnalyzerEngine(nlp_engine=nlp_engine)
|
||||||
|
return _PRESIDIO_ANALYZER
|
||||||
|
|
||||||
|
|
||||||
def _violation(
|
def _violation(
|
||||||
*,
|
*,
|
||||||
@@ -45,11 +72,11 @@ def _violation(
|
|||||||
def detect_pii(
|
def detect_pii(
|
||||||
text: str, config: dict[str, Any], trace_id: UUID, stage: str
|
text: str, config: dict[str, Any], trace_id: UUID, stage: str
|
||||||
) -> list[GuardrailViolation]:
|
) -> list[GuardrailViolation]:
|
||||||
"""Detección PII vía Presidio Analyzer (con fallback a regex si no está instalado)."""
|
"""Detección PII vía Presidio Analyzer (con fallback a regex si no está disponible)."""
|
||||||
try:
|
try:
|
||||||
from presidio_analyzer import AnalyzerEngine
|
analyzer = _presidio_analyzer()
|
||||||
except Exception:
|
except Exception:
|
||||||
# Si Presidio no está, fallback a regex básica
|
# Presidio no instalado o modelo spaCy no disponible → fallback a regex básica.
|
||||||
return _pii_regex_fallback(text, config, trace_id, stage)
|
return _pii_regex_fallback(text, config, trace_id, stage)
|
||||||
|
|
||||||
entities = config.get(
|
entities = config.get(
|
||||||
@@ -59,7 +86,6 @@ def detect_pii(
|
|||||||
sev = config.get("severity_on_match", "block")
|
sev = config.get("severity_on_match", "block")
|
||||||
blocked = sev == "block"
|
blocked = sev == "block"
|
||||||
|
|
||||||
analyzer = AnalyzerEngine()
|
|
||||||
results = analyzer.analyze(text=text, entities=entities, language="en")
|
results = analyzer.analyze(text=text, entities=entities, language="en")
|
||||||
if not results:
|
if not results:
|
||||||
return []
|
return []
|
||||||
|
|||||||
Reference in New Issue
Block a user