piifilter 0.1.0
Detect and mask PII in text before sending to LLMs, then reconstruct original values from responses.
Requires Python: >=3.9
Keywords: pii, privacy, llm, filter, masking

# pii-filter

A Python library that detects and masks PII (Personally Identifiable Information) in text before sending it to LLMs, then reconstructs the original values from LLM responses.

## Installation

```bash
pip install -e .
```

For the interactive demo (requires OpenAI):

```bash
pip install -e ".[demo]"
```

## How It Works

```
User text → filter() → sanitized text + session_id
                              ↓
                        Send to LLM (placeholders preserved)
                              ↓
LLM response → reconstruct(session_id, response) → original PII restored
```

Placeholders use the format `[TYPE_N]` — e.g. `[PAN_1]`, `[PHONE_2]`, `[EMAIL_3]`.

## Built-in Detectors

| Detector | Label | Accepted Formats | Validation | Spaced |
|---|---|---|---|---|
| Credit Card | `CREDIT_CARD` | Visa (`4xxx`), Mastercard (`51xx`–`55xx`), Amex (`34xx`/`37xx`), Discover (`6011`/`65xx`). 13–19 digits. | Luhn checksum | `4111 1111 1111 1111`, `4111-1111-1111-1111` |
| Phone | `PHONE` | **Indian:** `+91 XXXXXXXXXX`, `091-XXXXXXXXXX`, `0XXXXXXXXXX`, or bare 10 digits starting 6–9. **International:** `+CC NNNN...` (7–14 digits). | — | `+91 98765 43210`, `98765 43210` |
| Aadhaar | `AADHAAR` | 12 digits, first digit must be 2–9. Standard 4-4-4 grouping. | — | `2345 6789 0123`, `2345-6789-0123` |
| PAN Card | `PAN` | 5 uppercase letters + 4 digits + 1 uppercase letter (`AAAAA9999A`). | Word boundary | Not applicable |
| Driving License | `DL` | `SS RR YYYY NNNNNNN` — 2-letter state code, 2-digit RTO, 4-digit year (19xx/20xx), 7-digit serial. | Year prefix 19/20 | `DL 14 2011 0149646`, `DL-14-2011-0149646` |
| Email | `EMAIL` | Standard `local@domain.tld` format. Supports `.`, `+`, `%`, `-` in local part. | — | Not applicable |

## Usage

### Basic — All Detectors

```python
from pii_filter import PIIFilter

pf = PIIFilter()

sanitized, session_id = pf.filter("My PAN is ABCDE1234F, call +919876543210")
# sanitized: "My PAN is [PAN_1], call [PHONE_2]"

restored = pf.reconstruct(session_id, sanitized)
# restored: "My PAN is ABCDE1234F, call +919876543210"
```

### Selective Detectors

Only enable the detectors you need:

```python
pf = PIIFilter(detectors=["PAN", "EMAIL"])
```

Available labels: `CREDIT_CARD`, `PHONE`, `AADHAAR`, `PAN`, `DL`, `EMAIL`.

### Custom Patterns

Add your own regex-based detectors:

```python
pf = PIIFilter()
pf.add_custom_pattern("PASSPORT", r"[A-Z]\d{7}")

sanitized, sid = pf.filter("Passport: A1234567")
# sanitized: "Passport: [PASSPORT_1]"
```

With a validator function:

```python
pf.add_custom_pattern(
    "PASSPORT",
    r"[A-Z]\d{7}",
    validator=lambda s: len(s) == 8,
)
```

### Appending LLM Instructions

When sending to an LLM, append the placeholder-preservation instruction automatically:

```python
sanitized, sid = pf.filter("My PAN is ABCDE1234F", append_instruction=True)
```

This appends a block instructing the LLM to preserve all `[TYPE_N]` placeholders verbatim.

You can also retrieve the instruction text directly:

```python
instruction = PIIFilter.get_prompt_instruction()
```

### Session Cleanup

```python
pf.clear_session(session_id)   # remove one session
pf.clear_all_sessions()        # remove all sessions
```

## Demo Script

An interactive script that filters user input, sends it to OpenAI, and reconstructs the response:

```bash
export OPENAI_API_KEY="sk-..."
python demo.py
```

Optionally set the model:

```bash
export OPENAI_MODEL="gpt-4o"
python demo.py
```

## Development

```bash
pip install -e ".[dev]"
pytest --cov=pii_filter
```