File size: 2,293 Bytes
921d377 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | """
Safety rewriter — keeps generated text inside the active policy profile.
Phase 1 implementation: deterministic redaction + soft-rewrite for
flagged substrings. LLM phase will call an instructed rewriter
behind the same ``rewrite_for_safety`` signature.
Used by:
- script.generator after narration is written, to catch drift
- runtime (batch 6) free-input echo path
- publish (batch 8) pre-publish lint
"""
from __future__ import annotations
import re
from dataclasses import dataclass
from typing import List, Tuple
from ..policy.profiles import PolicyProfile
@dataclass(frozen=True)
class RewriteResult:
"""Outcome of a safety rewrite pass."""
text: str
changed: bool
removed_terms: List[str]
hints: List[str] # human-readable diagnostics (debugging aid)
# Fixed substitution list for universally-problematic tokens.
# These always redact regardless of profile.
_UNIVERSAL_REDACT: List[Tuple[re.Pattern[str], str]] = [
(re.compile(r"\b(child|minor|under\s*1[0-7])\b", re.I), "[redacted-age]"),
(re.compile(r"\b(kill|stab|rape)\b", re.I), "[redacted-violence]"),
]
def rewrite_for_safety(
text: str, profile: PolicyProfile,
) -> RewriteResult:
"""Apply universal redactions + profile-specific softening."""
if not text:
return RewriteResult(text="", changed=False, removed_terms=[], hints=[])
out = text
removed: List[str] = []
for pattern, replacement in _UNIVERSAL_REDACT:
new = pattern.sub(replacement, out)
if new != out:
removed.append(pattern.pattern)
out = new
# Profile-specific: if the profile's allowed list is narrow,
# strip any explicit language that slipped in.
if profile.allowed_intents and "explicit_request" not in profile.allowed_intents:
explicit = re.compile(
r"\b(pussy|cock|dick|nude|naked)\b", re.I,
)
new = explicit.sub("[redacted-explicit]", out)
if new != out:
removed.append("explicit_out_of_profile")
out = new
hints: List[str] = []
if removed:
hints.append(f"rewrote {len(removed)} problematic pattern(s)")
return RewriteResult(
text=out,
changed=out != text,
removed_terms=removed,
hints=hints,
)
|