openadapt-privacy 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. openadapt_privacy-0.1.0/.github/workflows/publish.yml +29 -0
  2. openadapt_privacy-0.1.0/.github/workflows/test.yml +54 -0
  3. openadapt_privacy-0.1.0/.gitignore +47 -0
  4. openadapt_privacy-0.1.0/PKG-INFO +276 -0
  5. openadapt_privacy-0.1.0/README.md +242 -0
  6. openadapt_privacy-0.1.0/assets/screenshot_original.png +0 -0
  7. openadapt_privacy-0.1.0/assets/screenshot_scrubbed.png +0 -0
  8. openadapt_privacy-0.1.0/openadapt_privacy/__init__.py +43 -0
  9. openadapt_privacy-0.1.0/openadapt_privacy/base.py +330 -0
  10. openadapt_privacy-0.1.0/openadapt_privacy/config.py +78 -0
  11. openadapt_privacy-0.1.0/openadapt_privacy/loaders.py +369 -0
  12. openadapt_privacy-0.1.0/openadapt_privacy/pipelines/__init__.py +13 -0
  13. openadapt_privacy-0.1.0/openadapt_privacy/pipelines/dicts.py +108 -0
  14. openadapt_privacy-0.1.0/openadapt_privacy/providers/__init__.py +71 -0
  15. openadapt_privacy-0.1.0/openadapt_privacy/providers/presidio.py +197 -0
  16. openadapt_privacy-0.1.0/pyproject.toml +66 -0
  17. openadapt_privacy-0.1.0/scripts/generate_demo_images.py +86 -0
  18. openadapt_privacy-0.1.0/tests/__init__.py +1 -0
  19. openadapt_privacy-0.1.0/tests/fixtures/__init__.py +144 -0
  20. openadapt_privacy-0.1.0/tests/test_base.py +140 -0
  21. openadapt_privacy-0.1.0/tests/test_config.py +59 -0
  22. openadapt_privacy-0.1.0/tests/test_fixtures.py +236 -0
  23. openadapt_privacy-0.1.0/tests/test_loaders.py +281 -0
  24. openadapt_privacy-0.1.0/tests/test_pipelines.py +167 -0
  25. openadapt_privacy-0.1.0/tests/test_presidio.py +133 -0
  26. openadapt_privacy-0.1.0/tests/test_providers.py +43 -0
@@ -0,0 +1,29 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ id-token: write # Required for trusted publishing
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v4
19
+ with:
20
+ version: "latest"
21
+
22
+ - name: Set up Python
23
+ run: uv python install 3.12
24
+
25
+ - name: Build package
26
+ run: uv build
27
+
28
+ - name: Publish to PyPI
29
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,54 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v4
21
+ with:
22
+ version: "latest"
23
+
24
+ - name: Set up Python ${{ matrix.python-version }}
25
+ run: uv python install ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --extra dev
29
+
30
+ - name: Run tests (without Presidio)
31
+ run: uv run pytest tests/ -v --ignore=tests/test_presidio.py
32
+
33
+ test-presidio:
34
+ runs-on: ubuntu-latest
35
+
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - name: Install uv
40
+ uses: astral-sh/setup-uv@v4
41
+ with:
42
+ version: "latest"
43
+
44
+ - name: Set up Python
45
+ run: uv python install 3.12
46
+
47
+ - name: Install dependencies with Presidio
48
+ run: |
49
+ uv sync --extra dev --extra presidio
50
+ uv pip install pip
51
+ uv run python -m spacy download en_core_web_trf
52
+
53
+ - name: Run all tests
54
+ run: uv run pytest tests/ -v
@@ -0,0 +1,47 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+
28
+ # Testing
29
+ .pytest_cache/
30
+ .coverage
31
+ htmlcov/
32
+ .tox/
33
+ .nox/
34
+
35
+ # IDEs
36
+ .idea/
37
+ .vscode/
38
+ *.swp
39
+ *.swo
40
+ *~
41
+
42
+ # OS
43
+ .DS_Store
44
+ Thumbs.db
45
+
46
+ # uv
47
+ uv.lock
@@ -0,0 +1,276 @@
1
+ Metadata-Version: 2.4
2
+ Name: openadapt-privacy
3
+ Version: 0.1.0
4
+ Summary: Privacy scrubbing for GUI automation data - PII/PHI detection and redaction
5
+ Project-URL: Homepage, https://github.com/OpenAdaptAI/openadapt-privacy
6
+ Project-URL: Repository, https://github.com/OpenAdaptAI/openadapt-privacy
7
+ Author-email: "MLDSAI Inc." <richard@mldsai.com>
8
+ License-Expression: MIT
9
+ Keywords: automation,gui,phi,pii,privacy,redaction,scrubbing
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: pillow>=10.0.0
21
+ Requires-Dist: pip>=25.3
22
+ Requires-Dist: pydantic>=2.0.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
25
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
26
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
27
+ Provides-Extra: presidio
28
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == 'presidio'
29
+ Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'presidio'
30
+ Requires-Dist: presidio-image-redactor>=0.0.50; extra == 'presidio'
31
+ Requires-Dist: spacy-transformers>=1.3.0; extra == 'presidio'
32
+ Requires-Dist: spacy>=3.7.0; extra == 'presidio'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # openadapt-privacy
36
+
37
+ Privacy scrubbing for GUI automation data - PII/PHI detection and redaction.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install openadapt-privacy
43
+ ```
44
+
45
+ For Presidio-based scrubbing (recommended):
46
+
47
+ ```bash
48
+ pip install openadapt-privacy[presidio]
49
+ python -m spacy download en_core_web_trf
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ### Text Scrubbing
55
+
56
+ ```python
57
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
58
+
59
+ scrubber = PresidioScrubbingProvider()
60
+
61
+ text = "Contact John Smith at john.smith@example.com or 555-123-4567"
62
+ scrubbed = scrubber.scrub_text(text)
63
+ ```
64
+
65
+ **Input:**
66
+ ```
67
+ Contact John Smith at john.smith@example.com or 555-123-4567
68
+ ```
69
+
70
+ **Output:**
71
+ ```
72
+ Contact <PERSON> at <EMAIL_ADDRESS> or <PHONE_NUMBER>
73
+ ```
74
+
75
+ ### Example Inputs & Outputs
76
+
77
+ | Input | Output |
78
+ |-------|--------|
79
+ | `My email is john@example.com` | `My email is <EMAIL_ADDRESS>` |
80
+ | `SSN: 923-45-6789` | `SSN: <US_SSN>` |
81
+ | `Card: 4532-1234-5678-9012` | `Card: <CREDIT_CARD>` |
82
+ | `Call me at 555-123-4567` | `Call me at <PHONE_NUMBER>` |
83
+ | `DOB: 01/15/1985` | `DOB: <DATE_TIME>` |
84
+ | `Contact John Smith` | `Contact <PERSON>` |
85
+
86
+ ## Dict Scrubbing
87
+
88
+ Scrub PII from nested dictionaries (e.g., GUI element trees):
89
+
90
+ ```python
91
+ from openadapt_privacy import scrub_dict
92
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
93
+
94
+ scrubber = PresidioScrubbingProvider()
95
+ action = {
96
+ "text": "Email: john@example.com",
97
+ "metadata": {
98
+ "title": "User Profile - John Smith",
99
+ "tooltip": "Click to contact john@example.com",
100
+ },
101
+ "coordinates": {"x": 100, "y": 200},
102
+ }
103
+ scrubbed = scrub_dict(action, scrubber)
104
+ ```
105
+
106
+ **Input:**
107
+ ```json
108
+ {
109
+ "text": "Email: john@example.com",
110
+ "metadata": {
111
+ "title": "User Profile - John Smith",
112
+ "tooltip": "Click to contact john@example.com"
113
+ },
114
+ "coordinates": {"x": 100, "y": 200}
115
+ }
116
+ ```
117
+
118
+ **Output:**
119
+ ```json
120
+ {
121
+ "text": "Email: <EMAIL_ADDRESS>",
122
+ "metadata": {
123
+ "title": "User Profile - <PERSON>",
124
+ "tooltip": "Click to contact <EMAIL_ADDRESS>"
125
+ },
126
+ "coordinates": {"x": 100, "y": 200}
127
+ }
128
+ ```
129
+
130
+ ## Recording Pipeline
131
+
132
+ Process complete GUI automation recordings:
133
+
134
+ ```python
135
+ from openadapt_privacy import DictRecordingLoader
136
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
137
+
138
+ scrubber = PresidioScrubbingProvider()
139
+ loader = DictRecordingLoader()
140
+
141
+ recording = loader.load_from_dict({
142
+ "task_description": "Send email to John Smith at john@example.com",
143
+ "actions": [
144
+ {"id": 1, "action_type": "click", "text": "Compose", "timestamp": 1000},
145
+ {"id": 2, "action_type": "type", "text": "john@example.com", "timestamp": 2000},
146
+ {"id": 3, "action_type": "click", "text": "Send", "window_title": "Email to john@example.com", "timestamp": 3000},
147
+ ],
148
+ })
149
+
150
+ scrubbed = recording.scrub(scrubber)
151
+ ```
152
+
153
+ **Input Recording:**
154
+ ```
155
+ task_description: "Send email to John Smith at john@example.com"
156
+
157
+ actions:
158
+ [1] click: "Compose"
159
+ [2] type: "john@example.com"
160
+ [3] click: "Send" (window: "Email to john@example.com")
161
+ ```
162
+
163
+ **Output Recording:**
164
+ ```
165
+ task_description: "Send email to <PERSON> at <EMAIL_ADDRESS>"
166
+
167
+ actions:
168
+ [1] click: "Compose"
169
+ [2] type: "<EMAIL_ADDRESS>"
170
+ [3] click: "Send" (window: "Email to <EMAIL_ADDRESS>")
171
+ ```
172
+
173
+ ## Image Scrubbing
174
+
175
+ Redact PII from screenshots using OCR + NER:
176
+
177
+ ```python
178
+ from PIL import Image
179
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
180
+
181
+ scrubber = PresidioScrubbingProvider()
182
+
183
+ image = Image.open("screenshot.png")
184
+ scrubbed_image = scrubber.scrub_image(image)
185
+ scrubbed_image.save("screenshot_scrubbed.png")
186
+ ```
187
+
188
+ **Input Screenshot:**
189
+
190
+ ![Original screenshot with PII](assets/screenshot_original.png)
191
+
192
+ **Output Screenshot:**
193
+
194
+ ![Scrubbed screenshot with PII redacted](assets/screenshot_scrubbed.png)
195
+
196
+ The image redactor:
197
+ 1. Runs OCR to detect text regions
198
+ 2. Analyzes text for PII entities (email, phone, SSN, etc.)
199
+ 3. Fills detected PII regions with solid color (configurable, default: red)
200
+
201
+ ## Custom Data Loader
202
+
203
+ Implement your own loader for custom storage formats:
204
+
205
+ ```python
206
+ from openadapt_privacy import RecordingLoader, Recording
207
+
208
+ class SQLiteRecordingLoader(RecordingLoader):
209
+ def __init__(self, db_path: str):
210
+ self.db_path = db_path
211
+
212
+ def load(self, recording_id: str) -> Recording:
213
+ # Load from SQLite database
214
+ ...
215
+
216
+ def save(self, recording: Recording, recording_id: str) -> None:
217
+ # Save to SQLite database
218
+ ...
219
+
220
+ # Usage
221
+ loader = SQLiteRecordingLoader("recordings.db")
222
+ scrubber = PresidioScrubbingProvider()
223
+
224
+ # Load, scrub, and save
225
+ scrubbed = loader.load_and_scrub("recording_001", scrubber)
226
+ loader.save(scrubbed, "recording_001_scrubbed")
227
+ ```
228
+
229
+ ## Configuration
230
+
231
+ ```python
232
+ from openadapt_privacy.config import PrivacyConfig
233
+
234
+ custom_config = PrivacyConfig(
235
+ SCRUB_CHAR="X", # Character for scrub_text_all
236
+ SCRUB_FILL_COLOR=0xFF0000, # Red for image redaction (BGR)
237
+ SCRUB_KEYS_HTML=[ # Keys to scrub in dicts
238
+ "text", "value", "title", "tooltip", "custom_field"
239
+ ],
240
+ SCRUB_PRESIDIO_IGNORE_ENTITIES=[ # Entity types to skip
241
+ "DATE_TIME",
242
+ ],
243
+ )
244
+ ```
245
+
246
+ ## Supported Entity Types
247
+
248
+ | Entity | Example Input | Example Output |
249
+ |--------|---------------|----------------|
250
+ | `PERSON` | `John Smith` | `<PERSON>` |
251
+ | `EMAIL_ADDRESS` | `john@example.com` | `<EMAIL_ADDRESS>` |
252
+ | `PHONE_NUMBER` | `555-123-4567` | `<PHONE_NUMBER>` |
253
+ | `US_SSN` | `923-45-6789` | `<US_SSN>` |
254
+ | `CREDIT_CARD` | `4532-1234-5678-9012` | `<CREDIT_CARD>` |
255
+ | `US_BANK_NUMBER` | `635526789012` | `<US_BANK_NUMBER>` |
256
+ | `US_DRIVER_LICENSE` | `A123-456-789-012` | `<US_DRIVER_LICENSE>` |
257
+ | `DATE_TIME` | `01/15/1985` | `<DATE_TIME>` |
258
+ | `LOCATION` | `Toronto, ON` | `<LOCATION>` |
259
+
260
+ ## Architecture
261
+
262
+ ```
263
+ openadapt_privacy/
264
+ ├── base.py # ScrubbingProvider, TextScrubbingMixin
265
+ ├── config.py # PrivacyConfig dataclass
266
+ ├── loaders.py # Recording, Action, Screenshot, RecordingLoader
267
+ ├── providers/
268
+ │ ├── __init__.py # ScrubProvider registry
269
+ │ └── presidio.py # PresidioScrubbingProvider
270
+ └── pipelines/
271
+ └── dicts.py # scrub_dict, scrub_list_dicts
272
+ ```
273
+
274
+ ## License
275
+
276
+ MIT
@@ -0,0 +1,242 @@
1
+ # openadapt-privacy
2
+
3
+ Privacy scrubbing for GUI automation data - PII/PHI detection and redaction.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install openadapt-privacy
9
+ ```
10
+
11
+ For Presidio-based scrubbing (recommended):
12
+
13
+ ```bash
14
+ pip install openadapt-privacy[presidio]
15
+ python -m spacy download en_core_web_trf
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ### Text Scrubbing
21
+
22
+ ```python
23
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
24
+
25
+ scrubber = PresidioScrubbingProvider()
26
+
27
+ text = "Contact John Smith at john.smith@example.com or 555-123-4567"
28
+ scrubbed = scrubber.scrub_text(text)
29
+ ```
30
+
31
+ **Input:**
32
+ ```
33
+ Contact John Smith at john.smith@example.com or 555-123-4567
34
+ ```
35
+
36
+ **Output:**
37
+ ```
38
+ Contact <PERSON> at <EMAIL_ADDRESS> or <PHONE_NUMBER>
39
+ ```
40
+
41
+ ### Example Inputs & Outputs
42
+
43
+ | Input | Output |
44
+ |-------|--------|
45
+ | `My email is john@example.com` | `My email is <EMAIL_ADDRESS>` |
46
+ | `SSN: 923-45-6789` | `SSN: <US_SSN>` |
47
+ | `Card: 4532-1234-5678-9012` | `Card: <CREDIT_CARD>` |
48
+ | `Call me at 555-123-4567` | `Call me at <PHONE_NUMBER>` |
49
+ | `DOB: 01/15/1985` | `DOB: <DATE_TIME>` |
50
+ | `Contact John Smith` | `Contact <PERSON>` |
51
+
52
+ ## Dict Scrubbing
53
+
54
+ Scrub PII from nested dictionaries (e.g., GUI element trees):
55
+
56
+ ```python
57
+ from openadapt_privacy import scrub_dict
58
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
59
+
60
+ scrubber = PresidioScrubbingProvider()
61
+ action = {
62
+ "text": "Email: john@example.com",
63
+ "metadata": {
64
+ "title": "User Profile - John Smith",
65
+ "tooltip": "Click to contact john@example.com",
66
+ },
67
+ "coordinates": {"x": 100, "y": 200},
68
+ }
69
+ scrubbed = scrub_dict(action, scrubber)
70
+ ```
71
+
72
+ **Input:**
73
+ ```json
74
+ {
75
+ "text": "Email: john@example.com",
76
+ "metadata": {
77
+ "title": "User Profile - John Smith",
78
+ "tooltip": "Click to contact john@example.com"
79
+ },
80
+ "coordinates": {"x": 100, "y": 200}
81
+ }
82
+ ```
83
+
84
+ **Output:**
85
+ ```json
86
+ {
87
+ "text": "Email: <EMAIL_ADDRESS>",
88
+ "metadata": {
89
+ "title": "User Profile - <PERSON>",
90
+ "tooltip": "Click to contact <EMAIL_ADDRESS>"
91
+ },
92
+ "coordinates": {"x": 100, "y": 200}
93
+ }
94
+ ```
95
+
96
+ ## Recording Pipeline
97
+
98
+ Process complete GUI automation recordings:
99
+
100
+ ```python
101
+ from openadapt_privacy import DictRecordingLoader
102
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
103
+
104
+ scrubber = PresidioScrubbingProvider()
105
+ loader = DictRecordingLoader()
106
+
107
+ recording = loader.load_from_dict({
108
+ "task_description": "Send email to John Smith at john@example.com",
109
+ "actions": [
110
+ {"id": 1, "action_type": "click", "text": "Compose", "timestamp": 1000},
111
+ {"id": 2, "action_type": "type", "text": "john@example.com", "timestamp": 2000},
112
+ {"id": 3, "action_type": "click", "text": "Send", "window_title": "Email to john@example.com", "timestamp": 3000},
113
+ ],
114
+ })
115
+
116
+ scrubbed = recording.scrub(scrubber)
117
+ ```
118
+
119
+ **Input Recording:**
120
+ ```
121
+ task_description: "Send email to John Smith at john@example.com"
122
+
123
+ actions:
124
+ [1] click: "Compose"
125
+ [2] type: "john@example.com"
126
+ [3] click: "Send" (window: "Email to john@example.com")
127
+ ```
128
+
129
+ **Output Recording:**
130
+ ```
131
+ task_description: "Send email to <PERSON> at <EMAIL_ADDRESS>"
132
+
133
+ actions:
134
+ [1] click: "Compose"
135
+ [2] type: "<EMAIL_ADDRESS>"
136
+ [3] click: "Send" (window: "Email to <EMAIL_ADDRESS>")
137
+ ```
138
+
139
+ ## Image Scrubbing
140
+
141
+ Redact PII from screenshots using OCR + NER:
142
+
143
+ ```python
144
+ from PIL import Image
145
+ from openadapt_privacy.providers.presidio import PresidioScrubbingProvider
146
+
147
+ scrubber = PresidioScrubbingProvider()
148
+
149
+ image = Image.open("screenshot.png")
150
+ scrubbed_image = scrubber.scrub_image(image)
151
+ scrubbed_image.save("screenshot_scrubbed.png")
152
+ ```
153
+
154
+ **Input Screenshot:**
155
+
156
+ ![Original screenshot with PII](assets/screenshot_original.png)
157
+
158
+ **Output Screenshot:**
159
+
160
+ ![Scrubbed screenshot with PII redacted](assets/screenshot_scrubbed.png)
161
+
162
+ The image redactor:
163
+ 1. Runs OCR to detect text regions
164
+ 2. Analyzes text for PII entities (email, phone, SSN, etc.)
165
+ 3. Fills detected PII regions with solid color (configurable, default: red)
166
+
167
+ ## Custom Data Loader
168
+
169
+ Implement your own loader for custom storage formats:
170
+
171
+ ```python
172
+ from openadapt_privacy import RecordingLoader, Recording
173
+
174
+ class SQLiteRecordingLoader(RecordingLoader):
175
+ def __init__(self, db_path: str):
176
+ self.db_path = db_path
177
+
178
+ def load(self, recording_id: str) -> Recording:
179
+ # Load from SQLite database
180
+ ...
181
+
182
+ def save(self, recording: Recording, recording_id: str) -> None:
183
+ # Save to SQLite database
184
+ ...
185
+
186
+ # Usage
187
+ loader = SQLiteRecordingLoader("recordings.db")
188
+ scrubber = PresidioScrubbingProvider()
189
+
190
+ # Load, scrub, and save
191
+ scrubbed = loader.load_and_scrub("recording_001", scrubber)
192
+ loader.save(scrubbed, "recording_001_scrubbed")
193
+ ```
194
+
195
+ ## Configuration
196
+
197
+ ```python
198
+ from openadapt_privacy.config import PrivacyConfig
199
+
200
+ custom_config = PrivacyConfig(
201
+ SCRUB_CHAR="X", # Character for scrub_text_all
202
+ SCRUB_FILL_COLOR=0xFF0000, # Red for image redaction (BGR)
203
+ SCRUB_KEYS_HTML=[ # Keys to scrub in dicts
204
+ "text", "value", "title", "tooltip", "custom_field"
205
+ ],
206
+ SCRUB_PRESIDIO_IGNORE_ENTITIES=[ # Entity types to skip
207
+ "DATE_TIME",
208
+ ],
209
+ )
210
+ ```
211
+
212
+ ## Supported Entity Types
213
+
214
+ | Entity | Example Input | Example Output |
215
+ |--------|---------------|----------------|
216
+ | `PERSON` | `John Smith` | `<PERSON>` |
217
+ | `EMAIL_ADDRESS` | `john@example.com` | `<EMAIL_ADDRESS>` |
218
+ | `PHONE_NUMBER` | `555-123-4567` | `<PHONE_NUMBER>` |
219
+ | `US_SSN` | `923-45-6789` | `<US_SSN>` |
220
+ | `CREDIT_CARD` | `4532-1234-5678-9012` | `<CREDIT_CARD>` |
221
+ | `US_BANK_NUMBER` | `635526789012` | `<US_BANK_NUMBER>` |
222
+ | `US_DRIVER_LICENSE` | `A123-456-789-012` | `<US_DRIVER_LICENSE>` |
223
+ | `DATE_TIME` | `01/15/1985` | `<DATE_TIME>` |
224
+ | `LOCATION` | `Toronto, ON` | `<LOCATION>` |
225
+
226
+ ## Architecture
227
+
228
+ ```
229
+ openadapt_privacy/
230
+ ├── base.py # ScrubbingProvider, TextScrubbingMixin
231
+ ├── config.py # PrivacyConfig dataclass
232
+ ├── loaders.py # Recording, Action, Screenshot, RecordingLoader
233
+ ├── providers/
234
+ │ ├── __init__.py # ScrubProvider registry
235
+ │ └── presidio.py # PresidioScrubbingProvider
236
+ └── pipelines/
237
+ └── dicts.py # scrub_dict, scrub_list_dicts
238
+ ```
239
+
240
+ ## License
241
+
242
+ MIT
@@ -0,0 +1,43 @@
1
+ """OpenAdapt Privacy - PII/PHI detection and redaction for GUI automation data."""
2
+
3
+ from openadapt_privacy.base import (
4
+ Modality,
5
+ ScrubbingProvider,
6
+ ScrubbingProviderFactory,
7
+ TextScrubbingMixin,
8
+ )
9
+ from openadapt_privacy.config import PrivacyConfig, config
10
+ from openadapt_privacy.loaders import (
11
+ Action,
12
+ DictRecordingLoader,
13
+ Recording,
14
+ RecordingLoader,
15
+ Screenshot,
16
+ )
17
+ from openadapt_privacy.pipelines.dicts import DictScrubber, scrub_dict, scrub_list_dicts
18
+ from openadapt_privacy.providers import ScrubProvider
19
+
20
+ __version__ = "0.1.0"
21
+
22
+ __all__ = [
23
+ # Base classes
24
+ "Modality",
25
+ "ScrubbingProvider",
26
+ "ScrubbingProviderFactory",
27
+ "TextScrubbingMixin",
28
+ # Config
29
+ "PrivacyConfig",
30
+ "config",
31
+ # Providers
32
+ "ScrubProvider",
33
+ # Pipelines
34
+ "DictScrubber",
35
+ "scrub_dict",
36
+ "scrub_list_dicts",
37
+ # Data loaders
38
+ "Action",
39
+ "Screenshot",
40
+ "Recording",
41
+ "RecordingLoader",
42
+ "DictRecordingLoader",
43
+ ]