SqueakyCleanText 0.6.1__tar.gz → 0.7.1__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.
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/PKG-INFO +148 -8
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/README.md +140 -2
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/SqueakyCleanText.egg-info/PKG-INFO +148 -8
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/SqueakyCleanText.egg-info/SOURCES.txt +28 -1
- squeakycleantext-0.7.1/SqueakyCleanText.egg-info/entry_points.txt +3 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/SqueakyCleanText.egg-info/requires.txt +7 -4
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/pyproject.toml +13 -6
- squeakycleantext-0.7.1/sct/__init__.py +71 -0
- squeakycleantext-0.7.1/sct/adapters.py +224 -0
- squeakycleantext-0.7.1/sct/browser.py +174 -0
- squeakycleantext-0.7.1/sct/cli.py +101 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/config.py +138 -8
- squeakycleantext-0.7.1/sct/evaluation.py +209 -0
- squeakycleantext-0.7.1/sct/mcp_server.py +104 -0
- squeakycleantext-0.7.1/sct/model_benchmark.py +175 -0
- squeakycleantext-0.7.1/sct/model_candidates.py +89 -0
- squeakycleantext-0.7.1/sct/ocr.py +123 -0
- squeakycleantext-0.7.1/sct/privacy.py +173 -0
- squeakycleantext-0.7.1/sct/risk.py +58 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/sct.py +286 -32
- squeakycleantext-0.7.1/sct/streaming.py +87 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/anonymization_map.py +4 -2
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/constants.py +63 -6
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/contact.py +36 -15
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/gliclass_adapter.py +10 -8
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/gliner_adapter.py +6 -3
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/ner.py +259 -58
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/onnx_pipeline.py +8 -1
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/process_result.py +3 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/stopwords.py +2 -2
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/torch_pipeline.py +11 -2
- squeakycleantext-0.7.1/tests/test_adapters.py +149 -0
- squeakycleantext-0.7.1/tests/test_benchmark_release.py +67 -0
- squeakycleantext-0.7.1/tests/test_browser.py +37 -0
- squeakycleantext-0.7.1/tests/test_chunking.py +170 -0
- squeakycleantext-0.7.1/tests/test_cli.py +79 -0
- squeakycleantext-0.7.1/tests/test_config_state.py +121 -0
- squeakycleantext-0.7.1/tests/test_contracts.py +136 -0
- squeakycleantext-0.7.1/tests/test_evaluation.py +129 -0
- squeakycleantext-0.7.1/tests/test_mcp_server.py +145 -0
- squeakycleantext-0.7.1/tests/test_model_benchmarks.py +119 -0
- squeakycleantext-0.7.1/tests/test_ocr.py +87 -0
- squeakycleantext-0.7.1/tests/test_privacy.py +401 -0
- squeakycleantext-0.7.1/tests/test_regex_boundaries.py +334 -0
- squeakycleantext-0.7.1/tests/test_risk.py +21 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/tests/test_sct.py +3 -2
- squeakycleantext-0.7.1/tests/test_streaming.py +34 -0
- squeakycleantext-0.6.1/sct/__init__.py +0 -13
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/LICENSE +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/SqueakyCleanText.egg-info/dependency_links.txt +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/SqueakyCleanText.egg-info/top_level.txt +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/__init__.py +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/datetime.py +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/normtext.py +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/resources.py +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/special.py +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/sct/utils/synthetic.py +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/setup.cfg +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/setup.py +0 -0
- {squeakycleantext-0.6.1 → squeakycleantext-0.7.1}/tests/test_functional.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SqueakyCleanText
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Text preprocessing & PII anonymization pipeline for NLP/ML: ONNX NER ensemble, language detection, stopword removal, and configurable token replacement.
|
|
5
5
|
Author: Rehan Fazal
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/rhnfzl/SqueakyCleanText
|
|
8
8
|
Project-URL: Repository, https://github.com/rhnfzl/SqueakyCleanText
|
|
9
9
|
Project-URL: Issues, https://github.com/rhnfzl/SqueakyCleanText/issues
|
|
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
-
Classifier:
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Operating System :: OS Independent
|
|
27
27
|
Requires-Python: >=3.11
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
@@ -32,7 +32,6 @@ Requires-Dist: stop-words>=2025.11.4
|
|
|
32
32
|
Requires-Dist: emoji>=2.14
|
|
33
33
|
Requires-Dist: ftfy>=6.3
|
|
34
34
|
Requires-Dist: Unidecode>=1.4
|
|
35
|
-
Requires-Dist: beautifulsoup4>=4.14
|
|
36
35
|
Requires-Dist: onnxruntime>=1.24.2
|
|
37
36
|
Requires-Dist: tokenizers>=0.22.2
|
|
38
37
|
Requires-Dist: huggingface-hub>=1.5.0
|
|
@@ -56,8 +55,6 @@ Provides-Extra: presidio
|
|
|
56
55
|
Requires-Dist: presidio-analyzer>=2.2.360; extra == "presidio"
|
|
57
56
|
Provides-Extra: classify
|
|
58
57
|
Requires-Dist: gliclass>=0.1.16; extra == "classify"
|
|
59
|
-
Provides-Extra: classify-onnx
|
|
60
|
-
Requires-Dist: onnxruntime>=1.24.2; extra == "classify-onnx"
|
|
61
58
|
Provides-Extra: all-ner
|
|
62
59
|
Requires-Dist: torch>=2.6.0; extra == "all-ner"
|
|
63
60
|
Requires-Dist: transformers>=4.48; extra == "all-ner"
|
|
@@ -73,6 +70,11 @@ Requires-Dist: rapidfuzz>=3.12; extra == "dev"
|
|
|
73
70
|
Provides-Extra: test
|
|
74
71
|
Requires-Dist: coverage>=7.8; extra == "test"
|
|
75
72
|
Requires-Dist: pytest-cov>=6.0; extra == "test"
|
|
73
|
+
Provides-Extra: mcp
|
|
74
|
+
Requires-Dist: fastmcp>=4.0.5; extra == "mcp"
|
|
75
|
+
Provides-Extra: ocr
|
|
76
|
+
Requires-Dist: pillow>=12.3.0; extra == "ocr"
|
|
77
|
+
Requires-Dist: pytesseract>=0.3.13; extra == "ocr"
|
|
76
78
|
Dynamic: license-file
|
|
77
79
|
|
|
78
80
|
<div align="center">
|
|
@@ -82,7 +84,7 @@ Dynamic: license-file
|
|
|
82
84
|
[](https://pypi.org/project/squeakycleantext/)
|
|
83
85
|
[](https://pypistats.org/packages/squeakycleantext)
|
|
84
86
|
[](https://github.com/rhnfzl/SqueakyCleanText/actions/workflows/python-package.yml)
|
|
85
|
-
[](https://pypi.org/project/squeakycleantext/)
|
|
87
|
+
[](https://pypi.org/project/squeakycleantext/)
|
|
86
88
|
[](LICENSE)
|
|
87
89
|
|
|
88
90
|
A comprehensive text cleaning and preprocessing pipeline for machine learning and NLP tasks.
|
|
@@ -179,6 +181,8 @@ The base install uses **ONNX Runtime** for NER inference - no PyTorch or Transfo
|
|
|
179
181
|
| Synthetic | `pip install SqueakyCleanText[synthetic]` | Faker-based synthetic replacement (realistic fake values instead of `<TAG>` tokens) |
|
|
180
182
|
| Presidio | `pip install SqueakyCleanText[presidio]` | Presidio-analyzer for `presidio_gliner` backend |
|
|
181
183
|
| Classify | `pip install SqueakyCleanText[classify]` | GLiClass document-level pre-classification |
|
|
184
|
+
| MCP | `pip install SqueakyCleanText[mcp]` | FastMCP server with text and JSON cleaning tools |
|
|
185
|
+
| OCR | `pip install SqueakyCleanText[ocr]` | Pillow image redaction and Tesseract OCR adapter |
|
|
182
186
|
| All NER | `pip install SqueakyCleanText[all-ner]` | All NER backends combined |
|
|
183
187
|
| Development | `pip install SqueakyCleanText[dev]` | Testing and linting tools |
|
|
184
188
|
|
|
@@ -304,7 +308,7 @@ lm_text, stat_text, lang = cleaner.process(
|
|
|
304
308
|
# Entities are anonymized: names, SSNs, emails, dates of birth, and 50+ more PII types
|
|
305
309
|
```
|
|
306
310
|
|
|
307
|
-
PII mode auto-configures: `ner_backend='gliner'`, uses [`knowledgator/gliner-pii-base-v1.0`](https://huggingface.co/knowledgator/gliner-pii-base-v1.0), sets
|
|
311
|
+
PII mode auto-configures: `ner_backend='gliner'`, uses [`knowledgator/gliner-pii-base-v1.0`](https://huggingface.co/knowledgator/gliner-pii-base-v1.0), sets `gliner_threshold` and the final `ner_confidence_threshold` to 0.3 (recall-focused), and expands positional tags. User-provided values always take priority.
|
|
308
312
|
|
|
309
313
|
**Alternative PII models** (pass as `gliner_model`):
|
|
310
314
|
|
|
@@ -588,11 +592,14 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
588
592
|
| `check_replace_html` | `True` | Strip/replace HTML tags |
|
|
589
593
|
| `check_replace_urls` | `True` | Replace URLs with token |
|
|
590
594
|
| `check_replace_emails` | `True` | Replace emails with token |
|
|
595
|
+
| `check_replace_ips` | `True` | Replace IPv4/IPv6 addresses (runs before dates, phones, years) |
|
|
596
|
+
| `replace_ips_public_only` | `False` | Keep private, loopback and reserved IPs; replace only public ones |
|
|
591
597
|
| `check_replace_years` | `True` | Replace years (1900-2099) |
|
|
592
598
|
| `check_replace_dates` | `False` | Replace full dates (ISO 8601, month names) |
|
|
593
599
|
| `check_fuzzy_replace_dates` | `False` | Fuzzy match misspelled months (requires `[fuzzy]`) |
|
|
594
600
|
| `check_replace_phone_numbers` | `True` | Replace phone numbers |
|
|
595
601
|
| `check_replace_numbers` | `True` | Replace standalone numbers |
|
|
602
|
+
| `replace_numbers_in_words` | `False` | Also replace digits inside Latin/Greek/Cyrillic words (`GPT4` to `GPT<NUMBER>`) |
|
|
596
603
|
| `check_replace_currency_symbols` | `True` | Replace currency symbols |
|
|
597
604
|
| `check_ner_process` | `True` | Run NER entity recognition |
|
|
598
605
|
| `check_remove_isolated_letters` | `True` | Remove single letters |
|
|
@@ -615,6 +622,7 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
615
622
|
| `replace_with_url` | `"<URL>"` |
|
|
616
623
|
| `replace_with_html` | `"<HTML>"` |
|
|
617
624
|
| `replace_with_email` | `"<EMAIL>"` |
|
|
625
|
+
| `replace_with_ips` | `"<IP>"` |
|
|
618
626
|
| `replace_with_years` | `"<YEAR>"` |
|
|
619
627
|
| `replace_with_dates` | `"<DATE>"` |
|
|
620
628
|
| `replace_with_phone_numbers` | `"<PHONE>"` |
|
|
@@ -653,6 +661,121 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
653
661
|
|
|
654
662
|
</details>
|
|
655
663
|
|
|
664
|
+
## Privacy Engine
|
|
665
|
+
|
|
666
|
+
Applications can apply versioned policy rules and inspect each decision.
|
|
667
|
+
This makes privacy behavior testable across services, datasets, and model upgrades.
|
|
668
|
+
|
|
669
|
+
```python
|
|
670
|
+
from sct import EntityRule, PrivacyPolicy, TextCleaner, TextCleanerConfig
|
|
671
|
+
|
|
672
|
+
policy = PrivacyPolicy(
|
|
673
|
+
name="support-export",
|
|
674
|
+
version="1",
|
|
675
|
+
rules={
|
|
676
|
+
"PERSON": EntityRule(action="redact", min_score=0.8),
|
|
677
|
+
"ORGANISATION": EntityRule(action="keep"),
|
|
678
|
+
},
|
|
679
|
+
allowlist=frozenset({"Jane Public"}),
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
cleaner = TextCleaner(
|
|
683
|
+
cfg=TextCleanerConfig(ner_mode="pii"),
|
|
684
|
+
privacy_policy=policy,
|
|
685
|
+
include_entities=True,
|
|
686
|
+
)
|
|
687
|
+
result = cleaner.process("Contact Maria Chen for help.")
|
|
688
|
+
|
|
689
|
+
print(result.metadata["findings"])
|
|
690
|
+
print(result.metadata["detector"])
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
Finding offsets (`start`, `end`) and NER entries in the reversible map are
|
|
694
|
+
document-relative positions in the post-regex text: the text after the regex
|
|
695
|
+
stages replaced URLs, emails, phones and the like. They are not offsets into the
|
|
696
|
+
raw input.
|
|
697
|
+
|
|
698
|
+
Reversible output is pseudonymized personal data. Anyone with the token map can
|
|
699
|
+
restore the source values. Keep token maps outside logs and cleaned datasets.
|
|
700
|
+
|
|
701
|
+
Use a `TokenStore` implementation to keep maps behind opaque references:
|
|
702
|
+
|
|
703
|
+
```python
|
|
704
|
+
from sct import InMemoryTokenStore, TextCleaner, TextCleanerConfig
|
|
705
|
+
|
|
706
|
+
store = InMemoryTokenStore()
|
|
707
|
+
cleaner = TextCleaner(
|
|
708
|
+
cfg=TextCleanerConfig(replacement_mode="reversible"),
|
|
709
|
+
token_store=store,
|
|
710
|
+
)
|
|
711
|
+
result = cleaner.process("Maria lives in Utrecht.")
|
|
712
|
+
reference = result.metadata["anon_map_reference"]
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
`InMemoryTokenStore` supports tests and short sessions. Production systems
|
|
716
|
+
should implement `TokenStore` with encryption, access control, expiry, and audit logs.
|
|
717
|
+
|
|
718
|
+
### Boundary adapters
|
|
719
|
+
|
|
720
|
+
The base package includes JSON, DataFrame, retrieval, command-line, and streaming adapters:
|
|
721
|
+
|
|
722
|
+
```python
|
|
723
|
+
from sct import RAGChunk, process_json, process_rag_chunks
|
|
724
|
+
|
|
725
|
+
cleaned_json = process_json(cleaner, {"message": "Email a@example.com"})
|
|
726
|
+
cleaned_chunks = process_rag_chunks(
|
|
727
|
+
cleaner,
|
|
728
|
+
[RAGChunk(id="1", text="Call +1-202-555-0147", metadata={})],
|
|
729
|
+
)
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
```sh
|
|
733
|
+
printf 'Email maria@example.com' | sct-clean --no-ner
|
|
734
|
+
printf 'Email maria@example.com' | sct-clean --no-ner \
|
|
735
|
+
--replacement-mode reversible --token-map tokens.json
|
|
736
|
+
pip install 'SqueakyCleanText[mcp]'
|
|
737
|
+
sct-mcp
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
`BufferedStreamingRedactor` holds a full document for cross-chunk NER safety.
|
|
741
|
+
`WindowedStreamingRedactor` provides bounded latency for contact tokens.
|
|
742
|
+
|
|
743
|
+
The OCR adapter accepts any `OCRProvider`. The included Tesseract provider also
|
|
744
|
+
needs a local Tesseract executable. Browser export writes an ONNX Runtime Web
|
|
745
|
+
manifest, but it does not copy model files or Python language detection.
|
|
746
|
+
|
|
747
|
+
Corpus owners can call `assess_reidentification_risk` to measure k-anonymity
|
|
748
|
+
for selected quasi-identifiers. Reports contain counts only, not source values.
|
|
749
|
+
|
|
750
|
+
## PII Evaluation
|
|
751
|
+
|
|
752
|
+
Model changes need ground-truth privacy evaluation, not only backend agreement.
|
|
753
|
+
The evaluation harness reports exact entity metrics by label, language, domain,
|
|
754
|
+
and named slice.
|
|
755
|
+
|
|
756
|
+
```sh
|
|
757
|
+
python -m scripts.evaluate_pii \
|
|
758
|
+
--dataset evaluation/pii_starter.jsonl \
|
|
759
|
+
--predictions path/to/model-predictions.jsonl \
|
|
760
|
+
--model model-name-and-revision \
|
|
761
|
+
--output evaluation/results/model-name.json
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
The included starter corpus is synthetic and only checks the evaluation path.
|
|
765
|
+
See [`evaluation/README.md`](evaluation/README.md) for schemas and release rules.
|
|
766
|
+
|
|
767
|
+
Pinned candidates live in `sct.model_candidates`. Run one candidate with:
|
|
768
|
+
|
|
769
|
+
```sh
|
|
770
|
+
python -m scripts.benchmark_models \
|
|
771
|
+
--dataset evaluation/pii_starter.jsonl \
|
|
772
|
+
--candidate gliner2-pii \
|
|
773
|
+
--output evaluation/results/gliner2-pii.json
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
Published third-party results are reference data only. `assess_candidate`
|
|
777
|
+
requires local recall, exact-span F1, latency, and ONNX parity before adoption.
|
|
778
|
+
|
|
656
779
|
## Architecture
|
|
657
780
|
|
|
658
781
|
SqueakyCleanText processes text through a configurable pipeline of sequential steps:
|
|
@@ -690,6 +813,23 @@ Each step is toggled by a `TextCleanerConfig` field. The pipeline is built once
|
|
|
690
813
|
|
|
691
814
|
## What's New
|
|
692
815
|
|
|
816
|
+
**v0.7.0**
|
|
817
|
+
- **Privacy policies**: Apply versioned entity rules, allowlists, thresholds, and redaction actions.
|
|
818
|
+
- **Structured audit findings**: Inspect entity decisions, detector backends, and pinned model revisions.
|
|
819
|
+
- **Safer reversible processing**: Restore contact values and keep token maps behind an optional `TokenStore`.
|
|
820
|
+
- **Boundary adapters**: Clean JSON, DataFrame-like objects, RAG chunks, standard input, and MCP tool requests.
|
|
821
|
+
- **Privacy evaluation**: Score exact PII spans by label, language, domain, and custom slice.
|
|
822
|
+
- **Model adoption gates**: Compare pinned candidates using recall, F1, latency, and ONNX parity.
|
|
823
|
+
- **Extended media support**: Redact OCR regions, process bounded streams, and export browser deployment manifests.
|
|
824
|
+
- **Corpus risk indicators**: Measure k-anonymity for user-selected quasi-identifiers.
|
|
825
|
+
- **Supply-chain improvements**: Pin model revisions, publish through PyPI Trusted Publishing, and ship standard lockfiles.
|
|
826
|
+
- **Python 3.14**: Added CI coverage alongside Python 3.11 through 3.13.
|
|
827
|
+
|
|
828
|
+
Migration notes:
|
|
829
|
+
- `gliclass_onnx=True` now raises an error because the upstream package has no native ONNX loader.
|
|
830
|
+
- HTML processing now honors `replace_with_html` instead of silently stripping tags.
|
|
831
|
+
- Structured adapters reject reversible mode unless each token map can be retained safely.
|
|
832
|
+
|
|
693
833
|
**v0.6.0**
|
|
694
834
|
- **PII detection mode** (`ner_mode='pii'`): auto-configures GLiNER with 60+ PII entity labels (personal, financial, healthcare, identity, digital)
|
|
695
835
|
- **Synthetic replacement** (`replacement_mode='synthetic'`): Faker-generated realistic values instead of `<TAG>` placeholders, with per-document consistency
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://pypi.org/project/squeakycleantext/)
|
|
6
6
|
[](https://pypistats.org/packages/squeakycleantext)
|
|
7
7
|
[](https://github.com/rhnfzl/SqueakyCleanText/actions/workflows/python-package.yml)
|
|
8
|
-
[](https://pypi.org/project/squeakycleantext/)
|
|
8
|
+
[](https://pypi.org/project/squeakycleantext/)
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
|
|
11
11
|
A comprehensive text cleaning and preprocessing pipeline for machine learning and NLP tasks.
|
|
@@ -102,6 +102,8 @@ The base install uses **ONNX Runtime** for NER inference - no PyTorch or Transfo
|
|
|
102
102
|
| Synthetic | `pip install SqueakyCleanText[synthetic]` | Faker-based synthetic replacement (realistic fake values instead of `<TAG>` tokens) |
|
|
103
103
|
| Presidio | `pip install SqueakyCleanText[presidio]` | Presidio-analyzer for `presidio_gliner` backend |
|
|
104
104
|
| Classify | `pip install SqueakyCleanText[classify]` | GLiClass document-level pre-classification |
|
|
105
|
+
| MCP | `pip install SqueakyCleanText[mcp]` | FastMCP server with text and JSON cleaning tools |
|
|
106
|
+
| OCR | `pip install SqueakyCleanText[ocr]` | Pillow image redaction and Tesseract OCR adapter |
|
|
105
107
|
| All NER | `pip install SqueakyCleanText[all-ner]` | All NER backends combined |
|
|
106
108
|
| Development | `pip install SqueakyCleanText[dev]` | Testing and linting tools |
|
|
107
109
|
|
|
@@ -227,7 +229,7 @@ lm_text, stat_text, lang = cleaner.process(
|
|
|
227
229
|
# Entities are anonymized: names, SSNs, emails, dates of birth, and 50+ more PII types
|
|
228
230
|
```
|
|
229
231
|
|
|
230
|
-
PII mode auto-configures: `ner_backend='gliner'`, uses [`knowledgator/gliner-pii-base-v1.0`](https://huggingface.co/knowledgator/gliner-pii-base-v1.0), sets
|
|
232
|
+
PII mode auto-configures: `ner_backend='gliner'`, uses [`knowledgator/gliner-pii-base-v1.0`](https://huggingface.co/knowledgator/gliner-pii-base-v1.0), sets `gliner_threshold` and the final `ner_confidence_threshold` to 0.3 (recall-focused), and expands positional tags. User-provided values always take priority.
|
|
231
233
|
|
|
232
234
|
**Alternative PII models** (pass as `gliner_model`):
|
|
233
235
|
|
|
@@ -511,11 +513,14 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
511
513
|
| `check_replace_html` | `True` | Strip/replace HTML tags |
|
|
512
514
|
| `check_replace_urls` | `True` | Replace URLs with token |
|
|
513
515
|
| `check_replace_emails` | `True` | Replace emails with token |
|
|
516
|
+
| `check_replace_ips` | `True` | Replace IPv4/IPv6 addresses (runs before dates, phones, years) |
|
|
517
|
+
| `replace_ips_public_only` | `False` | Keep private, loopback and reserved IPs; replace only public ones |
|
|
514
518
|
| `check_replace_years` | `True` | Replace years (1900-2099) |
|
|
515
519
|
| `check_replace_dates` | `False` | Replace full dates (ISO 8601, month names) |
|
|
516
520
|
| `check_fuzzy_replace_dates` | `False` | Fuzzy match misspelled months (requires `[fuzzy]`) |
|
|
517
521
|
| `check_replace_phone_numbers` | `True` | Replace phone numbers |
|
|
518
522
|
| `check_replace_numbers` | `True` | Replace standalone numbers |
|
|
523
|
+
| `replace_numbers_in_words` | `False` | Also replace digits inside Latin/Greek/Cyrillic words (`GPT4` to `GPT<NUMBER>`) |
|
|
519
524
|
| `check_replace_currency_symbols` | `True` | Replace currency symbols |
|
|
520
525
|
| `check_ner_process` | `True` | Run NER entity recognition |
|
|
521
526
|
| `check_remove_isolated_letters` | `True` | Remove single letters |
|
|
@@ -538,6 +543,7 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
538
543
|
| `replace_with_url` | `"<URL>"` |
|
|
539
544
|
| `replace_with_html` | `"<HTML>"` |
|
|
540
545
|
| `replace_with_email` | `"<EMAIL>"` |
|
|
546
|
+
| `replace_with_ips` | `"<IP>"` |
|
|
541
547
|
| `replace_with_years` | `"<YEAR>"` |
|
|
542
548
|
| `replace_with_dates` | `"<DATE>"` |
|
|
543
549
|
| `replace_with_phone_numbers` | `"<PHONE>"` |
|
|
@@ -576,6 +582,121 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
576
582
|
|
|
577
583
|
</details>
|
|
578
584
|
|
|
585
|
+
## Privacy Engine
|
|
586
|
+
|
|
587
|
+
Applications can apply versioned policy rules and inspect each decision.
|
|
588
|
+
This makes privacy behavior testable across services, datasets, and model upgrades.
|
|
589
|
+
|
|
590
|
+
```python
|
|
591
|
+
from sct import EntityRule, PrivacyPolicy, TextCleaner, TextCleanerConfig
|
|
592
|
+
|
|
593
|
+
policy = PrivacyPolicy(
|
|
594
|
+
name="support-export",
|
|
595
|
+
version="1",
|
|
596
|
+
rules={
|
|
597
|
+
"PERSON": EntityRule(action="redact", min_score=0.8),
|
|
598
|
+
"ORGANISATION": EntityRule(action="keep"),
|
|
599
|
+
},
|
|
600
|
+
allowlist=frozenset({"Jane Public"}),
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
cleaner = TextCleaner(
|
|
604
|
+
cfg=TextCleanerConfig(ner_mode="pii"),
|
|
605
|
+
privacy_policy=policy,
|
|
606
|
+
include_entities=True,
|
|
607
|
+
)
|
|
608
|
+
result = cleaner.process("Contact Maria Chen for help.")
|
|
609
|
+
|
|
610
|
+
print(result.metadata["findings"])
|
|
611
|
+
print(result.metadata["detector"])
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Finding offsets (`start`, `end`) and NER entries in the reversible map are
|
|
615
|
+
document-relative positions in the post-regex text: the text after the regex
|
|
616
|
+
stages replaced URLs, emails, phones and the like. They are not offsets into the
|
|
617
|
+
raw input.
|
|
618
|
+
|
|
619
|
+
Reversible output is pseudonymized personal data. Anyone with the token map can
|
|
620
|
+
restore the source values. Keep token maps outside logs and cleaned datasets.
|
|
621
|
+
|
|
622
|
+
Use a `TokenStore` implementation to keep maps behind opaque references:
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
from sct import InMemoryTokenStore, TextCleaner, TextCleanerConfig
|
|
626
|
+
|
|
627
|
+
store = InMemoryTokenStore()
|
|
628
|
+
cleaner = TextCleaner(
|
|
629
|
+
cfg=TextCleanerConfig(replacement_mode="reversible"),
|
|
630
|
+
token_store=store,
|
|
631
|
+
)
|
|
632
|
+
result = cleaner.process("Maria lives in Utrecht.")
|
|
633
|
+
reference = result.metadata["anon_map_reference"]
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
`InMemoryTokenStore` supports tests and short sessions. Production systems
|
|
637
|
+
should implement `TokenStore` with encryption, access control, expiry, and audit logs.
|
|
638
|
+
|
|
639
|
+
### Boundary adapters
|
|
640
|
+
|
|
641
|
+
The base package includes JSON, DataFrame, retrieval, command-line, and streaming adapters:
|
|
642
|
+
|
|
643
|
+
```python
|
|
644
|
+
from sct import RAGChunk, process_json, process_rag_chunks
|
|
645
|
+
|
|
646
|
+
cleaned_json = process_json(cleaner, {"message": "Email a@example.com"})
|
|
647
|
+
cleaned_chunks = process_rag_chunks(
|
|
648
|
+
cleaner,
|
|
649
|
+
[RAGChunk(id="1", text="Call +1-202-555-0147", metadata={})],
|
|
650
|
+
)
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
```sh
|
|
654
|
+
printf 'Email maria@example.com' | sct-clean --no-ner
|
|
655
|
+
printf 'Email maria@example.com' | sct-clean --no-ner \
|
|
656
|
+
--replacement-mode reversible --token-map tokens.json
|
|
657
|
+
pip install 'SqueakyCleanText[mcp]'
|
|
658
|
+
sct-mcp
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
`BufferedStreamingRedactor` holds a full document for cross-chunk NER safety.
|
|
662
|
+
`WindowedStreamingRedactor` provides bounded latency for contact tokens.
|
|
663
|
+
|
|
664
|
+
The OCR adapter accepts any `OCRProvider`. The included Tesseract provider also
|
|
665
|
+
needs a local Tesseract executable. Browser export writes an ONNX Runtime Web
|
|
666
|
+
manifest, but it does not copy model files or Python language detection.
|
|
667
|
+
|
|
668
|
+
Corpus owners can call `assess_reidentification_risk` to measure k-anonymity
|
|
669
|
+
for selected quasi-identifiers. Reports contain counts only, not source values.
|
|
670
|
+
|
|
671
|
+
## PII Evaluation
|
|
672
|
+
|
|
673
|
+
Model changes need ground-truth privacy evaluation, not only backend agreement.
|
|
674
|
+
The evaluation harness reports exact entity metrics by label, language, domain,
|
|
675
|
+
and named slice.
|
|
676
|
+
|
|
677
|
+
```sh
|
|
678
|
+
python -m scripts.evaluate_pii \
|
|
679
|
+
--dataset evaluation/pii_starter.jsonl \
|
|
680
|
+
--predictions path/to/model-predictions.jsonl \
|
|
681
|
+
--model model-name-and-revision \
|
|
682
|
+
--output evaluation/results/model-name.json
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
The included starter corpus is synthetic and only checks the evaluation path.
|
|
686
|
+
See [`evaluation/README.md`](evaluation/README.md) for schemas and release rules.
|
|
687
|
+
|
|
688
|
+
Pinned candidates live in `sct.model_candidates`. Run one candidate with:
|
|
689
|
+
|
|
690
|
+
```sh
|
|
691
|
+
python -m scripts.benchmark_models \
|
|
692
|
+
--dataset evaluation/pii_starter.jsonl \
|
|
693
|
+
--candidate gliner2-pii \
|
|
694
|
+
--output evaluation/results/gliner2-pii.json
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
Published third-party results are reference data only. `assess_candidate`
|
|
698
|
+
requires local recall, exact-span F1, latency, and ONNX parity before adoption.
|
|
699
|
+
|
|
579
700
|
## Architecture
|
|
580
701
|
|
|
581
702
|
SqueakyCleanText processes text through a configurable pipeline of sequential steps:
|
|
@@ -613,6 +734,23 @@ Each step is toggled by a `TextCleanerConfig` field. The pipeline is built once
|
|
|
613
734
|
|
|
614
735
|
## What's New
|
|
615
736
|
|
|
737
|
+
**v0.7.0**
|
|
738
|
+
- **Privacy policies**: Apply versioned entity rules, allowlists, thresholds, and redaction actions.
|
|
739
|
+
- **Structured audit findings**: Inspect entity decisions, detector backends, and pinned model revisions.
|
|
740
|
+
- **Safer reversible processing**: Restore contact values and keep token maps behind an optional `TokenStore`.
|
|
741
|
+
- **Boundary adapters**: Clean JSON, DataFrame-like objects, RAG chunks, standard input, and MCP tool requests.
|
|
742
|
+
- **Privacy evaluation**: Score exact PII spans by label, language, domain, and custom slice.
|
|
743
|
+
- **Model adoption gates**: Compare pinned candidates using recall, F1, latency, and ONNX parity.
|
|
744
|
+
- **Extended media support**: Redact OCR regions, process bounded streams, and export browser deployment manifests.
|
|
745
|
+
- **Corpus risk indicators**: Measure k-anonymity for user-selected quasi-identifiers.
|
|
746
|
+
- **Supply-chain improvements**: Pin model revisions, publish through PyPI Trusted Publishing, and ship standard lockfiles.
|
|
747
|
+
- **Python 3.14**: Added CI coverage alongside Python 3.11 through 3.13.
|
|
748
|
+
|
|
749
|
+
Migration notes:
|
|
750
|
+
- `gliclass_onnx=True` now raises an error because the upstream package has no native ONNX loader.
|
|
751
|
+
- HTML processing now honors `replace_with_html` instead of silently stripping tags.
|
|
752
|
+
- Structured adapters reject reversible mode unless each token map can be retained safely.
|
|
753
|
+
|
|
616
754
|
**v0.6.0**
|
|
617
755
|
- **PII detection mode** (`ner_mode='pii'`): auto-configures GLiNER with 60+ PII entity labels (personal, financial, healthcare, identity, digital)
|
|
618
756
|
- **Synthetic replacement** (`replacement_mode='synthetic'`): Faker-generated realistic values instead of `<TAG>` placeholders, with per-document consistency
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SqueakyCleanText
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Text preprocessing & PII anonymization pipeline for NLP/ML: ONNX NER ensemble, language detection, stopword removal, and configurable token replacement.
|
|
5
5
|
Author: Rehan Fazal
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/rhnfzl/SqueakyCleanText
|
|
8
8
|
Project-URL: Repository, https://github.com/rhnfzl/SqueakyCleanText
|
|
9
9
|
Project-URL: Issues, https://github.com/rhnfzl/SqueakyCleanText/issues
|
|
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
-
Classifier:
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Operating System :: OS Independent
|
|
27
27
|
Requires-Python: >=3.11
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
@@ -32,7 +32,6 @@ Requires-Dist: stop-words>=2025.11.4
|
|
|
32
32
|
Requires-Dist: emoji>=2.14
|
|
33
33
|
Requires-Dist: ftfy>=6.3
|
|
34
34
|
Requires-Dist: Unidecode>=1.4
|
|
35
|
-
Requires-Dist: beautifulsoup4>=4.14
|
|
36
35
|
Requires-Dist: onnxruntime>=1.24.2
|
|
37
36
|
Requires-Dist: tokenizers>=0.22.2
|
|
38
37
|
Requires-Dist: huggingface-hub>=1.5.0
|
|
@@ -56,8 +55,6 @@ Provides-Extra: presidio
|
|
|
56
55
|
Requires-Dist: presidio-analyzer>=2.2.360; extra == "presidio"
|
|
57
56
|
Provides-Extra: classify
|
|
58
57
|
Requires-Dist: gliclass>=0.1.16; extra == "classify"
|
|
59
|
-
Provides-Extra: classify-onnx
|
|
60
|
-
Requires-Dist: onnxruntime>=1.24.2; extra == "classify-onnx"
|
|
61
58
|
Provides-Extra: all-ner
|
|
62
59
|
Requires-Dist: torch>=2.6.0; extra == "all-ner"
|
|
63
60
|
Requires-Dist: transformers>=4.48; extra == "all-ner"
|
|
@@ -73,6 +70,11 @@ Requires-Dist: rapidfuzz>=3.12; extra == "dev"
|
|
|
73
70
|
Provides-Extra: test
|
|
74
71
|
Requires-Dist: coverage>=7.8; extra == "test"
|
|
75
72
|
Requires-Dist: pytest-cov>=6.0; extra == "test"
|
|
73
|
+
Provides-Extra: mcp
|
|
74
|
+
Requires-Dist: fastmcp>=4.0.5; extra == "mcp"
|
|
75
|
+
Provides-Extra: ocr
|
|
76
|
+
Requires-Dist: pillow>=12.3.0; extra == "ocr"
|
|
77
|
+
Requires-Dist: pytesseract>=0.3.13; extra == "ocr"
|
|
76
78
|
Dynamic: license-file
|
|
77
79
|
|
|
78
80
|
<div align="center">
|
|
@@ -82,7 +84,7 @@ Dynamic: license-file
|
|
|
82
84
|
[](https://pypi.org/project/squeakycleantext/)
|
|
83
85
|
[](https://pypistats.org/packages/squeakycleantext)
|
|
84
86
|
[](https://github.com/rhnfzl/SqueakyCleanText/actions/workflows/python-package.yml)
|
|
85
|
-
[](https://pypi.org/project/squeakycleantext/)
|
|
87
|
+
[](https://pypi.org/project/squeakycleantext/)
|
|
86
88
|
[](LICENSE)
|
|
87
89
|
|
|
88
90
|
A comprehensive text cleaning and preprocessing pipeline for machine learning and NLP tasks.
|
|
@@ -179,6 +181,8 @@ The base install uses **ONNX Runtime** for NER inference - no PyTorch or Transfo
|
|
|
179
181
|
| Synthetic | `pip install SqueakyCleanText[synthetic]` | Faker-based synthetic replacement (realistic fake values instead of `<TAG>` tokens) |
|
|
180
182
|
| Presidio | `pip install SqueakyCleanText[presidio]` | Presidio-analyzer for `presidio_gliner` backend |
|
|
181
183
|
| Classify | `pip install SqueakyCleanText[classify]` | GLiClass document-level pre-classification |
|
|
184
|
+
| MCP | `pip install SqueakyCleanText[mcp]` | FastMCP server with text and JSON cleaning tools |
|
|
185
|
+
| OCR | `pip install SqueakyCleanText[ocr]` | Pillow image redaction and Tesseract OCR adapter |
|
|
182
186
|
| All NER | `pip install SqueakyCleanText[all-ner]` | All NER backends combined |
|
|
183
187
|
| Development | `pip install SqueakyCleanText[dev]` | Testing and linting tools |
|
|
184
188
|
|
|
@@ -304,7 +308,7 @@ lm_text, stat_text, lang = cleaner.process(
|
|
|
304
308
|
# Entities are anonymized: names, SSNs, emails, dates of birth, and 50+ more PII types
|
|
305
309
|
```
|
|
306
310
|
|
|
307
|
-
PII mode auto-configures: `ner_backend='gliner'`, uses [`knowledgator/gliner-pii-base-v1.0`](https://huggingface.co/knowledgator/gliner-pii-base-v1.0), sets
|
|
311
|
+
PII mode auto-configures: `ner_backend='gliner'`, uses [`knowledgator/gliner-pii-base-v1.0`](https://huggingface.co/knowledgator/gliner-pii-base-v1.0), sets `gliner_threshold` and the final `ner_confidence_threshold` to 0.3 (recall-focused), and expands positional tags. User-provided values always take priority.
|
|
308
312
|
|
|
309
313
|
**Alternative PII models** (pass as `gliner_model`):
|
|
310
314
|
|
|
@@ -588,11 +592,14 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
588
592
|
| `check_replace_html` | `True` | Strip/replace HTML tags |
|
|
589
593
|
| `check_replace_urls` | `True` | Replace URLs with token |
|
|
590
594
|
| `check_replace_emails` | `True` | Replace emails with token |
|
|
595
|
+
| `check_replace_ips` | `True` | Replace IPv4/IPv6 addresses (runs before dates, phones, years) |
|
|
596
|
+
| `replace_ips_public_only` | `False` | Keep private, loopback and reserved IPs; replace only public ones |
|
|
591
597
|
| `check_replace_years` | `True` | Replace years (1900-2099) |
|
|
592
598
|
| `check_replace_dates` | `False` | Replace full dates (ISO 8601, month names) |
|
|
593
599
|
| `check_fuzzy_replace_dates` | `False` | Fuzzy match misspelled months (requires `[fuzzy]`) |
|
|
594
600
|
| `check_replace_phone_numbers` | `True` | Replace phone numbers |
|
|
595
601
|
| `check_replace_numbers` | `True` | Replace standalone numbers |
|
|
602
|
+
| `replace_numbers_in_words` | `False` | Also replace digits inside Latin/Greek/Cyrillic words (`GPT4` to `GPT<NUMBER>`) |
|
|
596
603
|
| `check_replace_currency_symbols` | `True` | Replace currency symbols |
|
|
597
604
|
| `check_ner_process` | `True` | Run NER entity recognition |
|
|
598
605
|
| `check_remove_isolated_letters` | `True` | Remove single letters |
|
|
@@ -615,6 +622,7 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
615
622
|
| `replace_with_url` | `"<URL>"` |
|
|
616
623
|
| `replace_with_html` | `"<HTML>"` |
|
|
617
624
|
| `replace_with_email` | `"<EMAIL>"` |
|
|
625
|
+
| `replace_with_ips` | `"<IP>"` |
|
|
618
626
|
| `replace_with_years` | `"<YEAR>"` |
|
|
619
627
|
| `replace_with_dates` | `"<DATE>"` |
|
|
620
628
|
| `replace_with_phone_numbers` | `"<PHONE>"` |
|
|
@@ -653,6 +661,121 @@ new_cfg = dataclasses.replace(cfg, check_ner_process=False)
|
|
|
653
661
|
|
|
654
662
|
</details>
|
|
655
663
|
|
|
664
|
+
## Privacy Engine
|
|
665
|
+
|
|
666
|
+
Applications can apply versioned policy rules and inspect each decision.
|
|
667
|
+
This makes privacy behavior testable across services, datasets, and model upgrades.
|
|
668
|
+
|
|
669
|
+
```python
|
|
670
|
+
from sct import EntityRule, PrivacyPolicy, TextCleaner, TextCleanerConfig
|
|
671
|
+
|
|
672
|
+
policy = PrivacyPolicy(
|
|
673
|
+
name="support-export",
|
|
674
|
+
version="1",
|
|
675
|
+
rules={
|
|
676
|
+
"PERSON": EntityRule(action="redact", min_score=0.8),
|
|
677
|
+
"ORGANISATION": EntityRule(action="keep"),
|
|
678
|
+
},
|
|
679
|
+
allowlist=frozenset({"Jane Public"}),
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
cleaner = TextCleaner(
|
|
683
|
+
cfg=TextCleanerConfig(ner_mode="pii"),
|
|
684
|
+
privacy_policy=policy,
|
|
685
|
+
include_entities=True,
|
|
686
|
+
)
|
|
687
|
+
result = cleaner.process("Contact Maria Chen for help.")
|
|
688
|
+
|
|
689
|
+
print(result.metadata["findings"])
|
|
690
|
+
print(result.metadata["detector"])
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
Finding offsets (`start`, `end`) and NER entries in the reversible map are
|
|
694
|
+
document-relative positions in the post-regex text: the text after the regex
|
|
695
|
+
stages replaced URLs, emails, phones and the like. They are not offsets into the
|
|
696
|
+
raw input.
|
|
697
|
+
|
|
698
|
+
Reversible output is pseudonymized personal data. Anyone with the token map can
|
|
699
|
+
restore the source values. Keep token maps outside logs and cleaned datasets.
|
|
700
|
+
|
|
701
|
+
Use a `TokenStore` implementation to keep maps behind opaque references:
|
|
702
|
+
|
|
703
|
+
```python
|
|
704
|
+
from sct import InMemoryTokenStore, TextCleaner, TextCleanerConfig
|
|
705
|
+
|
|
706
|
+
store = InMemoryTokenStore()
|
|
707
|
+
cleaner = TextCleaner(
|
|
708
|
+
cfg=TextCleanerConfig(replacement_mode="reversible"),
|
|
709
|
+
token_store=store,
|
|
710
|
+
)
|
|
711
|
+
result = cleaner.process("Maria lives in Utrecht.")
|
|
712
|
+
reference = result.metadata["anon_map_reference"]
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
`InMemoryTokenStore` supports tests and short sessions. Production systems
|
|
716
|
+
should implement `TokenStore` with encryption, access control, expiry, and audit logs.
|
|
717
|
+
|
|
718
|
+
### Boundary adapters
|
|
719
|
+
|
|
720
|
+
The base package includes JSON, DataFrame, retrieval, command-line, and streaming adapters:
|
|
721
|
+
|
|
722
|
+
```python
|
|
723
|
+
from sct import RAGChunk, process_json, process_rag_chunks
|
|
724
|
+
|
|
725
|
+
cleaned_json = process_json(cleaner, {"message": "Email a@example.com"})
|
|
726
|
+
cleaned_chunks = process_rag_chunks(
|
|
727
|
+
cleaner,
|
|
728
|
+
[RAGChunk(id="1", text="Call +1-202-555-0147", metadata={})],
|
|
729
|
+
)
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
```sh
|
|
733
|
+
printf 'Email maria@example.com' | sct-clean --no-ner
|
|
734
|
+
printf 'Email maria@example.com' | sct-clean --no-ner \
|
|
735
|
+
--replacement-mode reversible --token-map tokens.json
|
|
736
|
+
pip install 'SqueakyCleanText[mcp]'
|
|
737
|
+
sct-mcp
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
`BufferedStreamingRedactor` holds a full document for cross-chunk NER safety.
|
|
741
|
+
`WindowedStreamingRedactor` provides bounded latency for contact tokens.
|
|
742
|
+
|
|
743
|
+
The OCR adapter accepts any `OCRProvider`. The included Tesseract provider also
|
|
744
|
+
needs a local Tesseract executable. Browser export writes an ONNX Runtime Web
|
|
745
|
+
manifest, but it does not copy model files or Python language detection.
|
|
746
|
+
|
|
747
|
+
Corpus owners can call `assess_reidentification_risk` to measure k-anonymity
|
|
748
|
+
for selected quasi-identifiers. Reports contain counts only, not source values.
|
|
749
|
+
|
|
750
|
+
## PII Evaluation
|
|
751
|
+
|
|
752
|
+
Model changes need ground-truth privacy evaluation, not only backend agreement.
|
|
753
|
+
The evaluation harness reports exact entity metrics by label, language, domain,
|
|
754
|
+
and named slice.
|
|
755
|
+
|
|
756
|
+
```sh
|
|
757
|
+
python -m scripts.evaluate_pii \
|
|
758
|
+
--dataset evaluation/pii_starter.jsonl \
|
|
759
|
+
--predictions path/to/model-predictions.jsonl \
|
|
760
|
+
--model model-name-and-revision \
|
|
761
|
+
--output evaluation/results/model-name.json
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
The included starter corpus is synthetic and only checks the evaluation path.
|
|
765
|
+
See [`evaluation/README.md`](evaluation/README.md) for schemas and release rules.
|
|
766
|
+
|
|
767
|
+
Pinned candidates live in `sct.model_candidates`. Run one candidate with:
|
|
768
|
+
|
|
769
|
+
```sh
|
|
770
|
+
python -m scripts.benchmark_models \
|
|
771
|
+
--dataset evaluation/pii_starter.jsonl \
|
|
772
|
+
--candidate gliner2-pii \
|
|
773
|
+
--output evaluation/results/gliner2-pii.json
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
Published third-party results are reference data only. `assess_candidate`
|
|
777
|
+
requires local recall, exact-span F1, latency, and ONNX parity before adoption.
|
|
778
|
+
|
|
656
779
|
## Architecture
|
|
657
780
|
|
|
658
781
|
SqueakyCleanText processes text through a configurable pipeline of sequential steps:
|
|
@@ -690,6 +813,23 @@ Each step is toggled by a `TextCleanerConfig` field. The pipeline is built once
|
|
|
690
813
|
|
|
691
814
|
## What's New
|
|
692
815
|
|
|
816
|
+
**v0.7.0**
|
|
817
|
+
- **Privacy policies**: Apply versioned entity rules, allowlists, thresholds, and redaction actions.
|
|
818
|
+
- **Structured audit findings**: Inspect entity decisions, detector backends, and pinned model revisions.
|
|
819
|
+
- **Safer reversible processing**: Restore contact values and keep token maps behind an optional `TokenStore`.
|
|
820
|
+
- **Boundary adapters**: Clean JSON, DataFrame-like objects, RAG chunks, standard input, and MCP tool requests.
|
|
821
|
+
- **Privacy evaluation**: Score exact PII spans by label, language, domain, and custom slice.
|
|
822
|
+
- **Model adoption gates**: Compare pinned candidates using recall, F1, latency, and ONNX parity.
|
|
823
|
+
- **Extended media support**: Redact OCR regions, process bounded streams, and export browser deployment manifests.
|
|
824
|
+
- **Corpus risk indicators**: Measure k-anonymity for user-selected quasi-identifiers.
|
|
825
|
+
- **Supply-chain improvements**: Pin model revisions, publish through PyPI Trusted Publishing, and ship standard lockfiles.
|
|
826
|
+
- **Python 3.14**: Added CI coverage alongside Python 3.11 through 3.13.
|
|
827
|
+
|
|
828
|
+
Migration notes:
|
|
829
|
+
- `gliclass_onnx=True` now raises an error because the upstream package has no native ONNX loader.
|
|
830
|
+
- HTML processing now honors `replace_with_html` instead of silently stripping tags.
|
|
831
|
+
- Structured adapters reject reversible mode unless each token map can be retained safely.
|
|
832
|
+
|
|
693
833
|
**v0.6.0**
|
|
694
834
|
- **PII detection mode** (`ner_mode='pii'`): auto-configures GLiNER with 60+ PII entity labels (personal, financial, healthcare, identity, digital)
|
|
695
835
|
- **Synthetic replacement** (`replacement_mode='synthetic'`): Faker-generated realistic values instead of `<TAG>` placeholders, with per-document consistency
|