ash0ts commited on
Commit
3ad3f59
1 Parent(s): 42f5474

workign PII guardrails in chat_app

Browse files
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", "PHONE_NUMBER", "SOCIALNUM"],
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
- "PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER",
49
- "LOCATION", "CREDIT_CARD", "US_SSN"
50
  ]
51
 
52
  # Get available entities dynamically
53
  available_entities = self.get_available_entities()
54
 
55
- # Validate selected entities
56
- invalid_entities = set(selected_entities) - set(available_entities)
 
 
57
  if invalid_entities:
58
- raise ValueError(f"Invalid entities: {invalid_entities}")
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 multiple spaces
130
  result = ''.join(chars)
131
- return ' '.join(result.split())
 
 
 
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
- safe = safe and response["safe"]
24
- summaries += (
25
- f"**{guardrail.__class__.__name__}**: {response['summary']}\n\n---\n\n"
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]