Guides
Custom Detectors
Build custom detectors by subclassing the Detector base class.
Custom Detectors
You can extend FastPII by creating custom detectors. A detector implements the Detector base class with detect() and validate() methods.
Detector base class
from fastpii.detectors.base import Detector
from fastpii.models import Finding
class Detector(ABC):
name: str
region: str
description: str
@abstractmethod
def detect(self, text: str) -> list[Finding]:
...
@abstractmethod
def validate(self, value: str) -> bool:
...Creating a custom detector
import re
from fastpii.detectors.base import Detector
from fastpii.models import Finding
class EmployeeIdDetector(Detector):
def __init__(self):
super().__init__(
name="employee_id",
region="us",
description="Employee ID in format EMP-XXXXX",
)
def detect(self, text: str) -> list[Finding]:
pattern = re.compile(r"\bEMP-(\d{5})\b")
findings = []
for match in pattern.finditer(text):
findings.append(Finding(
type=self.name,
value=match.group(),
start=match.start(),
end=match.end(),
confidence=0.85,
region=self.region,
metadata={"employee_number": match.group(1)},
))
return findings
def validate(self, value: str) -> bool:
return bool(re.fullmatch(r"EMP-\d{5}", value))Registering a custom detector
from fastpii import FastPII, DEFAULT_PRIORITY
from fastpii.countries.cz import CzechPack
engine = FastPII(priority=DEFAULT_PRIORITY)
engine.register(CzechPack())
custom_detector = EmployeeIdDetector()
engine.register_detector(custom_detector)
result = engine.detect("Contact EMP-12345 for details")
print(result.findings[0].value)
# EMP-12345Custom metadata extraction
Override _extract_metadata() to provide additional metadata during validation:
class EmployeeIdDetector(Detector):
def __init__(self):
super().__init__(
name="employee_id",
region="us",
description="Employee ID in format EMP-XXXXX",
)
def detect(self, text: str) -> list[Finding]:
pattern = re.compile(r"\bEMP-(\d{5})\b")
findings = []
for match in pattern.finditer(text):
findings.append(Finding(
type=self.name,
value=match.group(),
start=match.start(),
end=match.end(),
confidence=0.85,
region=self.region,
metadata={"employee_number": match.group(1)},
))
return findings
def validate(self, value: str) -> bool:
return bool(re.fullmatch(r"EMP-\d{5}", value))
def _extract_metadata(self, value: str) -> dict:
match = re.fullmatch(r"EMP-(\d{5})", value)
if match:
return {"employee_number": match.group(1), "checksum_valid": True}
return {"checksum_valid": False}Priority for custom detectors
Add your custom detector type to the priority dict when creating the engine:
priority = {
**DEFAULT_PRIORITY,
"employee_id": 75,
}
engine = FastPII(priority=priority)Higher priority values win overlap conflicts against lower-priority detectors.