Agents read and write text that may contain personal data: emails, phone numbers, government ids, payment cards. Before logging a conversation, sending it to a third-party model or storing it as memory, a privacy guardrail should redact these items, replacing each with a typed placeholder like [EMAIL] so that the text stays readable but the secret is gone. Pattern matching finds the candidates, but patterns alone produce false positives: any 16-digit number looks like a card. A Luhn checksum, the check built into real card numbers, filters out random digit strings.
Implement luhn_valid and redact_pii.
luhn_valid(digits) takes a string of digits and returns True iff the Luhn checksum is valid: from the right, double every second digit, subtract 9 from doubled values above 9, and the total must be divisible by 10. Strings with fewer than 13 or more than 19 digits are invalid.redact_pii(text) applies these replacements in this order and returns (redacted_text, counts) where counts maps each placeholder name ('CARD', 'SSN', 'EMAIL', 'PHONE') to the number of replacements made (only names with at least one replacement appear).\b(?:\d[ -]?){12,18}\d\b; replace with [CARD] only if the digits (separators removed) pass luhn_valid. 2. SSN: \b\d{3}-\d{2}-\d{4}\b to [SSN]. 3. Email: [\w.+-]+@[\w-]+\.[\w.-]+ to [EMAIL]. 4. Phone: \b\d{3}[-. ]\d{3}[-. ]\d{4}\b to [PHONE].Use re.sub with a function to decide per match whether to replace (for the card check) and to count replacements.
Doing cards first prevents their digits from being mistaken for phone numbers.
No results yet
Run your code to check it against the examples, or submit to run every test.