API reference¶
Generated from the apogee-ai-guardrails source with mkdocstrings. Every symbol below is exported from apogee_ai_guardrails, so it is part of the supported public surface.
Application¶
Application · DTOs¶
ApplyPolicyDTO
dataclass
¶
BenchDTO
dataclass
¶
RedactDTO
dataclass
¶
ScanDTO
dataclass
¶
Application · Use cases¶
ApplyPolicyUseCase
¶
ApplyPolicyUseCase(detectors, policy: RedactionPolicy)
Pipeline: scan → block_if_critical → redact → return.
Source code in apogee_ai_guardrails/application/use_cases/apply_policy_use_case.py
execute
async
¶
execute(text: str, direction: ScanDirection = INPUT, raise_on_block: bool = True) -> PolicyOutcome
Source code in apogee_ai_guardrails/application/use_cases/apply_policy_use_case.py
async def execute(
self,
text: str,
direction: ScanDirection = ScanDirection.INPUT,
raise_on_block: bool = True,
) -> PolicyOutcome:
report = await self._scan.execute(text, direction=direction)
blocked = any(
_ge(v.severity, self._policy.block_severity) for v in report.violations
)
if blocked:
blocked_report = GuardrailReport(
text=report.text,
direction=report.direction,
violations=report.violations,
blocked=True,
metadata=report.metadata,
)
if raise_on_block:
raise GuardrailBlockedException(
reason=f"{len(report.violations)} violation(s)",
severity=report.severity.value,
)
return PolicyOutcome(report=blocked_report, redacted_text="")
redacted = await self._redact.execute(text)
return PolicyOutcome(report=report, redacted_text=redacted)
BenchPipelineUseCase
¶
Synthetic load against ApplyPolicy: measure detect/redact rates.
execute
async
¶
Source code in apogee_ai_guardrails/application/use_cases/bench_pipeline_use_case.py
async def execute(self, inputs: int) -> dict[str, float]:
if inputs <= 0:
raise ValueError("inputs must be positive")
rng = random.Random(42)
registry = DetectorRegistry.default()
pipeline = ApplyPolicyUseCase(registry.all(), default_redaction_policy())
detected = 0
blocked = 0
start = time.perf_counter()
for _ in range(inputs):
r = rng.random()
if r < 0.4:
text = rng.choice(_BENIGN)
elif r < 0.85:
text = rng.choice(_PII_LACED)
else:
text = rng.choice(_INJECTION)
try:
outcome = await pipeline.execute(text, raise_on_block=False)
except Exception: # pragma: no cover
continue
if outcome.report.violations:
detected += 1
if outcome.report.blocked:
blocked += 1
elapsed = (time.perf_counter() - start) * 1000.0
return {
"inputs": float(inputs),
"detected": float(detected),
"blocked": float(blocked),
"detection_rate": detected / inputs,
"elapsed_ms": elapsed,
"throughput_per_sec": (inputs / elapsed * 1000.0) if elapsed > 0 else 0.0,
}
ListDetectorsUseCase
¶
Source code in apogee_ai_guardrails/application/use_cases/list_detectors_use_case.py
execute
async
¶
RedactTextUseCase
¶
RedactTextUseCase(detectors: Iterable, policy: RedactionPolicy | None = None)
Applies the policy strategies on a per-violation basis.
Source code in apogee_ai_guardrails/application/use_cases/redact_text_use_case.py
def __init__(self, detectors: Iterable, policy: RedactionPolicy | None = None) -> None:
self._detectors = list(detectors)
self._policy = policy or default_redaction_policy()
self._mask = MaskRedactor(mask_char=self._policy.mask_char)
self._hash = HashRedactor()
self._format = FormatPreservingRedactor()
execute
async
¶
Source code in apogee_ai_guardrails/application/use_cases/redact_text_use_case.py
async def execute(self, text: str) -> str:
violations: list[GuardrailViolation] = []
for d in self._detectors:
violations.extend(await d.detect(text))
if not violations:
return text
grouped: dict[RedactionStrategy, list[GuardrailViolation]] = defaultdict(list)
for v in violations:
grouped[self._policy.strategy_for(v.kind)].append(v)
# Apply right-to-left so spans don't shift. We sort the union once.
all_with_strategy = sorted(
((v, strat) for strat, vs in grouped.items() for v in vs),
key=lambda item: item[0].span_start,
reverse=True,
)
out = text
for v, strategy in all_with_strategy:
replacement = self._render(v, strategy)
out = out[: v.span_start] + replacement + out[v.span_end :]
return out
ScanTextUseCase
¶
Runs every detector against the text and aggregates violations.
Source code in apogee_ai_guardrails/application/use_cases/scan_text_use_case.py
execute
async
¶
execute(text: str, direction: ScanDirection = INPUT) -> GuardrailReport
Source code in apogee_ai_guardrails/application/use_cases/scan_text_use_case.py
async def execute(
self,
text: str,
direction: ScanDirection = ScanDirection.INPUT,
) -> GuardrailReport:
all_violations: list[GuardrailViolation] = []
for detector in self._detectors:
all_violations.extend(await detector.detect(text))
return GuardrailReport(
text=text,
direction=direction,
violations=tuple(all_violations),
metadata={"detectors": ",".join(d.name for d in self._detectors)},
)
Domain¶
GuardrailReport
dataclass
¶
GuardrailReport(text: str, direction: ScanDirection = INPUT, violations: tuple[GuardrailViolation, ...] = (), blocked: bool = False, metadata: dict[str, str] = dict())
violations
class-attribute
instance-attribute
¶
violations: tuple[GuardrailViolation, ...] = ()
metadata
class-attribute
instance-attribute
¶
GuardrailViolation
dataclass
¶
GuardrailViolation(kind: PiiKind, detector: str, span_start: int, span_end: int, matched_text: str, severity: RuleSeverity = MEDIUM, confidence: float = 1.0, metadata: dict[str, str] = dict())
metadata
class-attribute
instance-attribute
¶
RedactionPolicy
dataclass
¶
RedactionPolicy(name: str = 'default', default_strategy: RedactionStrategy = MASK, per_kind: dict[PiiKind, RedactionStrategy] = dict(), block_severity: RuleSeverity = CRITICAL, mask_char: str = '*')
default_strategy
class-attribute
instance-attribute
¶
default_strategy: RedactionStrategy = MASK
per_kind
class-attribute
instance-attribute
¶
per_kind: dict[PiiKind, RedactionStrategy] = field(default_factory=dict)
strategy_for
¶
strategy_for(kind: PiiKind) -> RedactionStrategy
RuleSeverity
¶
Bases: str, Enum
ScanDirection
¶
Domain · Enums¶
PiiKind
¶
Bases: str, Enum
Domain · Exceptions¶
GuardrailBlockedException
¶
Bases: GuardrailError
Source code in apogee_ai_guardrails/domain/exceptions/guardrail_exceptions.py
GuardrailError
¶
Bases: Exception
Base for apogee-ai-guardrails errors.
PolicyViolationException
¶
Bases: GuardrailError
Source code in apogee_ai_guardrails/domain/exceptions/guardrail_exceptions.py
Domain · Protocols (ports)¶
IDetector
¶
Bases: Protocol
detect
async
¶
detect(text: str) -> list[GuardrailViolation]
IGuardrailPipeline
¶
Bases: Protocol
scan
async
¶
scan(text: str) -> GuardrailReport
IRedactor
¶
Bases: Protocol
redact
¶
redact(text: str, violations: Iterable[GuardrailViolation], policy: RedactionPolicy | None = None) -> str
Infrastructure¶
DetectorRegistry
¶
FormatPreservingRedactor
¶
Replaces digits with X / letters with x, keeping punctuation intact.
Useful when downstream parsing depends on the original layout (e.g. CPF
123.456.789-09 becomes XXX.XXX.XXX-XX).
redact
¶
redact(text: str, violations: Iterable[GuardrailViolation], policy: RedactionPolicy | None = None) -> str
Source code in apogee_ai_guardrails/infrastructure/redactors/format_preserving_redactor.py
HashRedactor
¶
Replaces matched spans with <KIND:hash8> for deterministic correlation.
Source code in apogee_ai_guardrails/infrastructure/redactors/hash_redactor.py
redact
¶
redact(text: str, violations: Iterable[GuardrailViolation], policy: RedactionPolicy | None = None) -> str
Source code in apogee_ai_guardrails/infrastructure/redactors/hash_redactor.py
JailbreakDetector
¶
JailbreakDetector(severity: RuleSeverity = CRITICAL)
Source code in apogee_ai_guardrails/infrastructure/detectors/jailbreak_detector.py
detect
async
¶
detect(text: str) -> list[GuardrailViolation]
Source code in apogee_ai_guardrails/infrastructure/detectors/jailbreak_detector.py
async def detect(self, text: str) -> list[GuardrailViolation]:
out: list[GuardrailViolation] = []
for pat in _PATTERNS:
for m in pat.finditer(text):
out.append(
GuardrailViolation(
kind=PiiKind.JAILBREAK,
detector=self.name,
span_start=m.start(),
span_end=m.end(),
matched_text=m.group(0),
severity=self._severity,
confidence=0.9,
)
)
return out
MaskRedactor
¶
Replaces matched text with a single repeated mask char of equal length.
Source code in apogee_ai_guardrails/infrastructure/redactors/mask_redactor.py
redact
¶
redact(text: str, violations: Iterable[GuardrailViolation], policy: RedactionPolicy | None = None) -> str
Source code in apogee_ai_guardrails/infrastructure/redactors/mask_redactor.py
PiiDetector
¶
PiiDetector(kinds: tuple[PiiKind, ...] | None = None)
Regex-based PII scanner for PT-BR + EN patterns. Zero external deps.
Source code in apogee_ai_guardrails/infrastructure/detectors/pii_detector.py
detect
async
¶
detect(text: str) -> list[GuardrailViolation]
Source code in apogee_ai_guardrails/infrastructure/detectors/pii_detector.py
async def detect(self, text: str) -> list[GuardrailViolation]:
out: list[GuardrailViolation] = []
for kind in self._kinds:
pattern, severity = _PATTERNS[kind]
for match in pattern.finditer(text):
# Skip IPs that are fully zero (likely not real PII).
if kind is PiiKind.IP_V4 and match.group(0) == "0.0.0.0":
continue
out.append(
GuardrailViolation(
kind=kind,
detector=self.name,
span_start=match.start(),
span_end=match.end(),
matched_text=match.group(0),
severity=severity,
confidence=0.95,
)
)
return out
PresidioPiiDetector
¶
Lazy adapter for Microsoft Presidio. install via extras=presidio.
Source code in apogee_ai_guardrails/infrastructure/detectors/presidio_pii_detector.py
detect
async
¶
detect(text: str) -> list[GuardrailViolation]
Source code in apogee_ai_guardrails/infrastructure/detectors/presidio_pii_detector.py
async def detect(self, text: str) -> list[GuardrailViolation]:
self._ensure()
results = self._analyzer.analyze(text=text, language=self._language) # type: ignore[union-attr]
out: list[GuardrailViolation] = []
for r in results:
out.append(
GuardrailViolation(
kind=PiiKind.OTHER,
detector=self.name,
span_start=int(r.start),
span_end=int(r.end),
matched_text=text[r.start : r.end],
severity=RuleSeverity.HIGH,
confidence=float(r.score),
metadata={"entity_type": str(r.entity_type)},
)
)
return out
ProfanityDetector
¶
ProfanityDetector(terms: tuple[str, ...] = _DEFAULT_TERMS, severity: RuleSeverity = LOW)
Source code in apogee_ai_guardrails/infrastructure/detectors/profanity_detector.py
def __init__(
self,
terms: tuple[str, ...] = _DEFAULT_TERMS,
severity: RuleSeverity = RuleSeverity.LOW,
) -> None:
if not terms:
raise ValueError("terms list cannot be empty")
self._terms = tuple(t.lower() for t in terms)
self._severity = severity
joined = "|".join(re.escape(t) for t in self._terms)
self._pattern = re.compile(rf"\b({joined})\b", re.IGNORECASE)
detect
async
¶
detect(text: str) -> list[GuardrailViolation]
Source code in apogee_ai_guardrails/infrastructure/detectors/profanity_detector.py
PromptInjectionDetector
¶
PromptInjectionDetector(severity: RuleSeverity = HIGH)
Source code in apogee_ai_guardrails/infrastructure/detectors/prompt_injection_detector.py
detect
async
¶
detect(text: str) -> list[GuardrailViolation]
Source code in apogee_ai_guardrails/infrastructure/detectors/prompt_injection_detector.py
async def detect(self, text: str) -> list[GuardrailViolation]:
out: list[GuardrailViolation] = []
for pat in _PATTERNS:
for m in pat.finditer(text):
out.append(
GuardrailViolation(
kind=PiiKind.PROMPT_INJECTION,
detector=self.name,
span_start=m.start(),
span_end=m.end(),
matched_text=m.group(0),
severity=self._severity,
confidence=0.85,
metadata={"pattern": pat.pattern[:60]},
)
)
return out
SchemaDetector
¶
SchemaDetector(required_keys: tuple[str, ...] = (), severity: RuleSeverity = HIGH)
Validates that the text parses as JSON and contains required keys.
Lightweight — no jsonschema dependency. Required keys are matched against the top-level object only.
Source code in apogee_ai_guardrails/infrastructure/detectors/schema_detector.py
detect
async
¶
detect(text: str) -> list[GuardrailViolation]
Source code in apogee_ai_guardrails/infrastructure/detectors/schema_detector.py
async def detect(self, text: str) -> list[GuardrailViolation]:
try:
payload = json.loads(text)
except json.JSONDecodeError as exc:
return [
GuardrailViolation(
kind=PiiKind.SCHEMA_VIOLATION,
detector=self.name,
span_start=0,
span_end=max(len(text), 1),
matched_text=text[:100],
severity=self._severity,
confidence=1.0,
metadata={"reason": f"invalid json: {exc.msg}"},
)
]
if not isinstance(payload, dict):
return [
GuardrailViolation(
kind=PiiKind.SCHEMA_VIOLATION,
detector=self.name,
span_start=0,
span_end=max(len(text), 1),
matched_text=text[:100],
severity=self._severity,
confidence=1.0,
metadata={"reason": "expected json object at top level"},
)
]
missing = [k for k in self._required if k not in payload]
if missing:
return [
GuardrailViolation(
kind=PiiKind.SCHEMA_VIOLATION,
detector=self.name,
span_start=0,
span_end=max(len(text), 1),
matched_text=", ".join(missing),
severity=self._severity,
confidence=1.0,
metadata={"reason": "missing required keys"},
)
]
return []
default_redaction_policy
¶
default_redaction_policy() -> RedactionPolicy
Sensible defaults: format-preserving for IDs, mask for everything else.
Source code in apogee_ai_guardrails/infrastructure/policies/default_policy.py
def default_redaction_policy() -> RedactionPolicy:
"""Sensible defaults: format-preserving for IDs, mask for everything else."""
return RedactionPolicy(
name="default",
default_strategy=RedactionStrategy.MASK,
per_kind={
PiiKind.CPF: RedactionStrategy.FORMAT_PRESERVING,
PiiKind.CNPJ: RedactionStrategy.FORMAT_PRESERVING,
PiiKind.RG: RedactionStrategy.FORMAT_PRESERVING,
PiiKind.CREDIT_CARD: RedactionStrategy.FORMAT_PRESERVING,
PiiKind.SSN: RedactionStrategy.FORMAT_PRESERVING,
PiiKind.EMAIL: RedactionStrategy.HASH,
PiiKind.JAILBREAK: RedactionStrategy.REMOVE,
PiiKind.PROMPT_INJECTION: RedactionStrategy.REMOVE,
},
block_severity=RuleSeverity.CRITICAL,
)