Spaces:
Runtime error
Runtime error
workign PII guardrails in chat_app
Browse files- application_pages/chat_app.py +21 -0
- guardrails_genie/guardrails/__init__.py +8 -0
- guardrails_genie/guardrails/entity_recognition/__init__.py +9 -0
- guardrails_genie/guardrails/entity_recognition/pii_examples/run_transformers.py +1 -1
- guardrails_genie/guardrails/entity_recognition/presidio_entity_recognition_guardrail.py +29 -7
- guardrails_genie/guardrails/entity_recognition/regex_entity_recognition_guardrail.py +8 -0
- guardrails_genie/guardrails/entity_recognition/transformers_entity_recognition_guardrail.py +13 -2
- guardrails_genie/guardrails/manager.py +7 -4
- pyproject.toml +2 -0
application_pages/chat_app.py
CHANGED
|
@@ -61,6 +61,27 @@ def initialize_guardrails():
|
|
| 61 |
guardrail_name,
|
| 62 |
)(model_name=classifier_model_name)
|
| 63 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
st.session_state.guardrails_manager = GuardrailManager(
|
| 65 |
guardrails=st.session_state.guardrails
|
| 66 |
)
|
|
|
|
| 61 |
guardrail_name,
|
| 62 |
)(model_name=classifier_model_name)
|
| 63 |
)
|
| 64 |
+
elif guardrail_name == "PresidioEntityRecognitionGuardrail":
|
| 65 |
+
st.session_state.guardrails.append(
|
| 66 |
+
getattr(
|
| 67 |
+
importlib.import_module("guardrails_genie.guardrails"),
|
| 68 |
+
guardrail_name,
|
| 69 |
+
)()
|
| 70 |
+
)
|
| 71 |
+
elif guardrail_name == "RegexEntityRecognitionGuardrail":
|
| 72 |
+
st.session_state.guardrails.append(
|
| 73 |
+
getattr(
|
| 74 |
+
importlib.import_module("guardrails_genie.guardrails"),
|
| 75 |
+
guardrail_name,
|
| 76 |
+
)()
|
| 77 |
+
)
|
| 78 |
+
elif guardrail_name == "TransformersEntityRecognitionGuardrail":
|
| 79 |
+
st.session_state.guardrails.append(
|
| 80 |
+
getattr(
|
| 81 |
+
importlib.import_module("guardrails_genie.guardrails"),
|
| 82 |
+
guardrail_name,
|
| 83 |
+
)()
|
| 84 |
+
)
|
| 85 |
st.session_state.guardrails_manager = GuardrailManager(
|
| 86 |
guardrails=st.session_state.guardrails
|
| 87 |
)
|
guardrails_genie/guardrails/__init__.py
CHANGED
|
@@ -2,10 +2,18 @@ from .injection import (
|
|
| 2 |
PromptInjectionClassifierGuardrail,
|
| 3 |
PromptInjectionSurveyGuardrail,
|
| 4 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
from .manager import GuardrailManager
|
| 6 |
|
| 7 |
__all__ = [
|
| 8 |
"PromptInjectionSurveyGuardrail",
|
| 9 |
"PromptInjectionClassifierGuardrail",
|
|
|
|
|
|
|
|
|
|
| 10 |
"GuardrailManager",
|
| 11 |
]
|
|
|
|
| 2 |
PromptInjectionClassifierGuardrail,
|
| 3 |
PromptInjectionSurveyGuardrail,
|
| 4 |
)
|
| 5 |
+
from .entity_recognition import (
|
| 6 |
+
PresidioEntityRecognitionGuardrail,
|
| 7 |
+
RegexEntityRecognitionGuardrail,
|
| 8 |
+
TransformersEntityRecognitionGuardrail,
|
| 9 |
+
)
|
| 10 |
from .manager import GuardrailManager
|
| 11 |
|
| 12 |
__all__ = [
|
| 13 |
"PromptInjectionSurveyGuardrail",
|
| 14 |
"PromptInjectionClassifierGuardrail",
|
| 15 |
+
"PresidioEntityRecognitionGuardrail",
|
| 16 |
+
"RegexEntityRecognitionGuardrail",
|
| 17 |
+
"TransformersEntityRecognitionGuardrail",
|
| 18 |
"GuardrailManager",
|
| 19 |
]
|
guardrails_genie/guardrails/entity_recognition/__init__.py
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .presidio_entity_recognition_guardrail import PresidioEntityRecognitionGuardrail
|
| 2 |
+
from .regex_entity_recognition_guardrail import RegexEntityRecognitionGuardrail
|
| 3 |
+
from .transformers_entity_recognition_guardrail import TransformersEntityRecognitionGuardrail
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
"PresidioEntityRecognitionGuardrail",
|
| 7 |
+
"RegexEntityRecognitionGuardrail",
|
| 8 |
+
"TransformersEntityRecognitionGuardrail",
|
| 9 |
+
]
|
guardrails_genie/guardrails/entity_recognition/pii_examples/run_transformers.py
CHANGED
|
@@ -8,7 +8,7 @@ def test_pii_detection():
|
|
| 8 |
|
| 9 |
# Create the guardrail with default entities and anonymization enabled
|
| 10 |
pii_guardrail = TransformersEntityRecognitionGuardrail(
|
| 11 |
-
selected_entities=["GIVENNAME", "SURNAME", "EMAIL", "
|
| 12 |
should_anonymize=True,
|
| 13 |
show_available_entities=True
|
| 14 |
)
|
|
|
|
| 8 |
|
| 9 |
# Create the guardrail with default entities and anonymization enabled
|
| 10 |
pii_guardrail = TransformersEntityRecognitionGuardrail(
|
| 11 |
+
selected_entities=["GIVENNAME", "SURNAME", "EMAIL", "TELEPHONENUM", "SOCIALNUM"],
|
| 12 |
should_anonymize=True,
|
| 13 |
show_available_entities=True
|
| 14 |
)
|
guardrails_genie/guardrails/entity_recognition/presidio_entity_recognition_guardrail.py
CHANGED
|
@@ -13,11 +13,19 @@ class PresidioEntityRecognitionResponse(BaseModel):
|
|
| 13 |
explanation: str
|
| 14 |
anonymized_text: Optional[str] = None
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
class PresidioEntityRecognitionSimpleResponse(BaseModel):
|
| 17 |
contains_entities: bool
|
| 18 |
explanation: str
|
| 19 |
anonymized_text: Optional[str] = None
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
#TODO: Add support for transformers workflow and not just Spacy
|
| 22 |
class PresidioEntityRecognitionGuardrail(Guardrail):
|
| 23 |
@staticmethod
|
|
@@ -40,23 +48,37 @@ class PresidioEntityRecognitionGuardrail(Guardrail):
|
|
| 40 |
language: str = "en",
|
| 41 |
deny_lists: Optional[Dict[str, List[str]]] = None,
|
| 42 |
regex_patterns: Optional[Dict[str, List[Dict[str, str]]]] = None,
|
| 43 |
-
custom_recognizers: Optional[List[Any]] = None
|
|
|
|
| 44 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
# Initialize default values
|
| 46 |
if selected_entities is None:
|
| 47 |
selected_entities = [
|
| 48 |
-
"
|
| 49 |
-
"
|
| 50 |
]
|
| 51 |
|
| 52 |
# Get available entities dynamically
|
| 53 |
available_entities = self.get_available_entities()
|
| 54 |
|
| 55 |
-
#
|
| 56 |
-
invalid_entities =
|
|
|
|
|
|
|
| 57 |
if invalid_entities:
|
| 58 |
-
|
| 59 |
-
|
|
|
|
|
|
|
| 60 |
# Initialize analyzer with default recognizers
|
| 61 |
analyzer = AnalyzerEngine()
|
| 62 |
|
|
|
|
| 13 |
explanation: str
|
| 14 |
anonymized_text: Optional[str] = None
|
| 15 |
|
| 16 |
+
@property
|
| 17 |
+
def safe(self) -> bool:
|
| 18 |
+
return not self.contains_entities
|
| 19 |
+
|
| 20 |
class PresidioEntityRecognitionSimpleResponse(BaseModel):
|
| 21 |
contains_entities: bool
|
| 22 |
explanation: str
|
| 23 |
anonymized_text: Optional[str] = None
|
| 24 |
|
| 25 |
+
@property
|
| 26 |
+
def safe(self) -> bool:
|
| 27 |
+
return not self.contains_entities
|
| 28 |
+
|
| 29 |
#TODO: Add support for transformers workflow and not just Spacy
|
| 30 |
class PresidioEntityRecognitionGuardrail(Guardrail):
|
| 31 |
@staticmethod
|
|
|
|
| 48 |
language: str = "en",
|
| 49 |
deny_lists: Optional[Dict[str, List[str]]] = None,
|
| 50 |
regex_patterns: Optional[Dict[str, List[Dict[str, str]]]] = None,
|
| 51 |
+
custom_recognizers: Optional[List[Any]] = None,
|
| 52 |
+
show_available_entities: bool = False
|
| 53 |
):
|
| 54 |
+
# If show_available_entities is True, print available entities
|
| 55 |
+
if show_available_entities:
|
| 56 |
+
available_entities = self.get_available_entities()
|
| 57 |
+
print("\nAvailable entities:")
|
| 58 |
+
print("=" * 25)
|
| 59 |
+
for entity in available_entities:
|
| 60 |
+
print(f"- {entity}")
|
| 61 |
+
print("=" * 25 + "\n")
|
| 62 |
+
|
| 63 |
# Initialize default values
|
| 64 |
if selected_entities is None:
|
| 65 |
selected_entities = [
|
| 66 |
+
"CREDIT_CARD", "US_SSN", "EMAIL_ADDRESS", "PHONE_NUMBER",
|
| 67 |
+
"IP_ADDRESS", "URL", "DATE_TIME"
|
| 68 |
]
|
| 69 |
|
| 70 |
# Get available entities dynamically
|
| 71 |
available_entities = self.get_available_entities()
|
| 72 |
|
| 73 |
+
# Filter out invalid entities and warn user
|
| 74 |
+
invalid_entities = [e for e in selected_entities if e not in available_entities]
|
| 75 |
+
valid_entities = [e for e in selected_entities if e in available_entities]
|
| 76 |
+
|
| 77 |
if invalid_entities:
|
| 78 |
+
print(f"\nWarning: The following entities are not available and will be ignored: {invalid_entities}")
|
| 79 |
+
print(f"Continuing with valid entities: {valid_entities}")
|
| 80 |
+
selected_entities = valid_entities
|
| 81 |
+
|
| 82 |
# Initialize analyzer with default recognizers
|
| 83 |
analyzer = AnalyzerEngine()
|
| 84 |
|
guardrails_genie/guardrails/entity_recognition/regex_entity_recognition_guardrail.py
CHANGED
|
@@ -13,12 +13,20 @@ class RegexEntityRecognitionResponse(BaseModel):
|
|
| 13 |
explanation: str
|
| 14 |
anonymized_text: Optional[str] = None
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
class RegexEntityRecognitionSimpleResponse(BaseModel):
|
| 18 |
contains_entities: bool
|
| 19 |
explanation: str
|
| 20 |
anonymized_text: Optional[str] = None
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
class RegexEntityRecognitionGuardrail(Guardrail):
|
| 24 |
regex_model: RegexModel
|
|
|
|
| 13 |
explanation: str
|
| 14 |
anonymized_text: Optional[str] = None
|
| 15 |
|
| 16 |
+
@property
|
| 17 |
+
def safe(self) -> bool:
|
| 18 |
+
return not self.contains_entities
|
| 19 |
+
|
| 20 |
|
| 21 |
class RegexEntityRecognitionSimpleResponse(BaseModel):
|
| 22 |
contains_entities: bool
|
| 23 |
explanation: str
|
| 24 |
anonymized_text: Optional[str] = None
|
| 25 |
|
| 26 |
+
@property
|
| 27 |
+
def safe(self) -> bool:
|
| 28 |
+
return not self.contains_entities
|
| 29 |
+
|
| 30 |
|
| 31 |
class RegexEntityRecognitionGuardrail(Guardrail):
|
| 32 |
regex_model: RegexModel
|
guardrails_genie/guardrails/entity_recognition/transformers_entity_recognition_guardrail.py
CHANGED
|
@@ -11,11 +11,19 @@ class TransformersEntityRecognitionResponse(BaseModel):
|
|
| 11 |
explanation: str
|
| 12 |
anonymized_text: Optional[str] = None
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
class TransformersEntityRecognitionSimpleResponse(BaseModel):
|
| 15 |
contains_entities: bool
|
| 16 |
explanation: str
|
| 17 |
anonymized_text: Optional[str] = None
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
class TransformersEntityRecognitionGuardrail(Guardrail):
|
| 20 |
"""Generic guardrail for detecting entities using any token classification model."""
|
| 21 |
|
|
@@ -126,9 +134,12 @@ class TransformersEntityRecognitionGuardrail(Guardrail):
|
|
| 126 |
# Replace the entity with the redaction marker
|
| 127 |
chars[start:end] = replacement
|
| 128 |
|
| 129 |
-
# Join and clean up
|
| 130 |
result = ''.join(chars)
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
@weave.op()
|
| 134 |
def guard(self, prompt: str, return_detected_types: bool = True, aggregate_redaction: bool = True) -> TransformersEntityRecognitionResponse | TransformersEntityRecognitionSimpleResponse:
|
|
|
|
| 11 |
explanation: str
|
| 12 |
anonymized_text: Optional[str] = None
|
| 13 |
|
| 14 |
+
@property
|
| 15 |
+
def safe(self) -> bool:
|
| 16 |
+
return not self.contains_entities
|
| 17 |
+
|
| 18 |
class TransformersEntityRecognitionSimpleResponse(BaseModel):
|
| 19 |
contains_entities: bool
|
| 20 |
explanation: str
|
| 21 |
anonymized_text: Optional[str] = None
|
| 22 |
|
| 23 |
+
@property
|
| 24 |
+
def safe(self) -> bool:
|
| 25 |
+
return not self.contains_entities
|
| 26 |
+
|
| 27 |
class TransformersEntityRecognitionGuardrail(Guardrail):
|
| 28 |
"""Generic guardrail for detecting entities using any token classification model."""
|
| 29 |
|
|
|
|
| 134 |
# Replace the entity with the redaction marker
|
| 135 |
chars[start:end] = replacement
|
| 136 |
|
| 137 |
+
# Join characters and clean up only consecutive spaces (preserving newlines)
|
| 138 |
result = ''.join(chars)
|
| 139 |
+
# Replace multiple spaces with single space, but preserve newlines
|
| 140 |
+
lines = result.split('\n')
|
| 141 |
+
cleaned_lines = [' '.join(line.split()) for line in lines]
|
| 142 |
+
return '\n'.join(cleaned_lines)
|
| 143 |
|
| 144 |
@weave.op()
|
| 145 |
def guard(self, prompt: str, return_detected_types: bool = True, aggregate_redaction: bool = True) -> TransformersEntityRecognitionResponse | TransformersEntityRecognitionSimpleResponse:
|
guardrails_genie/guardrails/manager.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import weave
|
| 2 |
from rich.progress import track
|
|
|
|
| 3 |
|
| 4 |
from .base import Guardrail
|
| 5 |
|
|
@@ -20,10 +21,12 @@ class GuardrailManager(weave.Model):
|
|
| 20 |
alerts.append(
|
| 21 |
{"guardrail_name": guardrail.__class__.__name__, "response": response}
|
| 22 |
)
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
f"**{guardrail.__class__.__name__}**: {response
|
| 26 |
-
|
|
|
|
|
|
|
| 27 |
return {"safe": safe, "alerts": alerts, "summary": summaries}
|
| 28 |
|
| 29 |
@weave.op()
|
|
|
|
| 1 |
import weave
|
| 2 |
from rich.progress import track
|
| 3 |
+
from pydantic import BaseModel
|
| 4 |
|
| 5 |
from .base import Guardrail
|
| 6 |
|
|
|
|
| 21 |
alerts.append(
|
| 22 |
{"guardrail_name": guardrail.__class__.__name__, "response": response}
|
| 23 |
)
|
| 24 |
+
if isinstance(response, BaseModel):
|
| 25 |
+
safe = safe and response.safe
|
| 26 |
+
summaries += f"**{guardrail.__class__.__name__}**: {response.explanation}\n\n---\n\n"
|
| 27 |
+
else:
|
| 28 |
+
safe = safe and response["safe"]
|
| 29 |
+
summaries += f"**{guardrail.__class__.__name__}**: {response['summary']}\n\n---\n\n"
|
| 30 |
return {"safe": safe, "alerts": alerts, "summary": summaries}
|
| 31 |
|
| 32 |
@weave.op()
|
pyproject.toml
CHANGED
|
@@ -20,6 +20,8 @@ dependencies = [
|
|
| 20 |
"pymupdf4llm>=0.0.17",
|
| 21 |
"transformers>=4.46.3",
|
| 22 |
"torch>=2.5.1",
|
|
|
|
|
|
|
| 23 |
]
|
| 24 |
|
| 25 |
[tool.setuptools]
|
|
|
|
| 20 |
"pymupdf4llm>=0.0.17",
|
| 21 |
"transformers>=4.46.3",
|
| 22 |
"torch>=2.5.1",
|
| 23 |
+
"presidio-analyzer>=2.2.355",
|
| 24 |
+
"presidio-anonymizer>=2.2.355",
|
| 25 |
]
|
| 26 |
|
| 27 |
[tool.setuptools]
|