Spaces:
Running
Running
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]
|