API¶
TextCleaner¶
TextCleaner(cfg=None, *, privacy_policy=None, token_store=None, include_entities=False, metrics_callback=None, detector_registry=None, unified_policy=None)
Pass a TextCleanerConfig to change behavior. Omitting cfg uses its defaults; module-level configuration globals are no longer supported.
process(text: str, *, context: DetectionContext | None = None) -> ProcessResult¶
Cleans one text. It runs process_batch([text]) and returns its one result. A
ProcessResult unpacks as a 3-tuple (lm_text, stat_text, language):
- Cleaned text formatted for language models.
- Cleaned text formatted for statistical models (None if check_statistical_model_processing is False).
- Detected language of the text. None when it is unknown, or when no step needs it: check_detect_language, check_ner_process and check_remove_stopwords all False and no language set. NER and stopword removal run detection even with check_detect_language=False.
process_batch(texts, max_workers=None, *, contexts=None) -> list[ProcessResult]¶
Processes multiple texts using thread-parallel execution. Each result follows the same format as process().
max_workers is the maximum number of worker threads (default os.cpu_count(); it must be >= 1).
Use max_workers for document concurrency; the deprecated batch_size keyword is removed.
Empty or whitespace-only input returns ProcessResult("", "", None) with the empty_input warning,
and a non-string item or a non-empty plain str for texts raises ValueError. With NER on and ner_batch_size > 1, the steps before
NER run per document in threads, then one NER call batches the chunks of all documents.
With unified processing, contexts contains one DetectionContext per input, in input order.
An explicit context requires unified processing. The configuration and each context are immutable.
aprocess_batch(texts, max_workers=None, *, contexts=None) -> list[ProcessResult]¶
Async version of process_batch for use with asyncio-based frameworks (FastAPI, aiohttp). Runs the batch in a thread-pool executor so it does not block the event loop:
from sct import TextCleaner
cleaner = TextCleaner()
# In an async context (FastAPI route, aiohttp handler, etc.)
async def handler(texts):
return await cleaner.aprocess_batch(texts)
warmup(languages: Optional[List[str]] = None) -> None¶
Pre-loads legacy NER models to reduce first-request latency. Call once during application startup:
from sct import TextCleaner
cleaner = TextCleaner()
cleaner.warmup(['ENGLISH', 'DUTCH']) # or warmup() for all supported languages
Unified processing rejects warmup() with ValueError.
Its detector providers belong to each worker, so warming one thread cannot initialize providers created by later batch threads.
Prepare and validate the selected model artifacts before starting the application.
Unified workers still initialize their providers on first use; this method does not remove that startup cost.
Legacy model load failures produce a warning and remain possible on first use.
ProcessResult¶
Note:
ProcessResultfromprocess()unpacks as a 3-tuple (lm_text, stat_text, language) for backward compatibility, but also exposes.metadatafor reversible maps and document classification.metadatais always a dict with value-free counts:metadata['regex_counts'](e.g.{"EMAIL": {"count": 1, "chars": 17}}, one entry per regex replacement type) andmetadata['entity_counts'](NER findings per type whose value was concealed, same shape; entities a privacy policy kept, allowed or ignored are not counted). It also holdslanguage_confidence(Lingua top-1 confidence,1.0when pinned,Nonewhen unknown),language_source(pinned,detected,fallbackwhen alanguagetuple fell back to its first entry, ornone) andwarnings, a sorted list of codes:language_unknown,language_fallback,language_low_confidence,ner_fallback_models(the language has no own NER model and ran MULTILINGUAL + ENGLISH),cuda_fallback(CUDA was requested but a model ran on CPU),mps_fallback(the Apple GPU was requested but a torch or GLiNER model ran on CPU),custom_pattern_timeout(acustom_patternsregex timed out and was skipped,custom_patterns_on_timeout='warn'),empty_input.metadata['provenance']is{"backend", "models"}, one{"key", "id", "revision", "provider"}entry per NER model that runs for the document's language (revisionis the resolved Hub commit for ONNX models,providerthe ONNX execution provider or the torch device);{"backend": None, "models": []}when NER is off.
A NER model that cannot load raises sct.ModelLoadError, on the first text that needs it. GLiNER and Presidio backends raise it when the NER object is built, not on the first text; a missing extra for them stays ImportError.
result.signals is the value-free view of that metadata, with the same keys for every input:
language, language_confidence, language_source, chars, pii_count, pii_chars,
pii_share, pii_types, regex_counts, entity_counts, sensitive, warnings (adds
ner_off) and models. sensitive is pii_count > 0, so a replaced date, year, number,
currency or HTML tag alone is not sensitive. It never holds entity text, so it is safe to log or store next to
cleaned data. Score columns, sct-report, the adapters and the MCP server read it.
These count and offset descriptions apply to legacy processing. Unified processing adds versioned metadata and signals described below. Caller-chosen identifiers and model names can still identify private work; restrict access when required.
Unified findings and caller context¶
Users can apply one policy to format matches and model findings before any replacement.
Set pii_processing='unified' and pass unified_policy=UnifiedPrivacyPolicy(...) to select this path.
PrivacyPolicy remains the separate legacy NER policy; do not pass it to the unified path.
DetectionContext(document_id=None, path=(), declared_entity_types=(), schema_id=None) carries caller-owned context.
It does not infer field meanings. Direct processing forwards it to context-capable detectors.
FieldSchema(id, version, rules) and FieldRule(path, entity_types, mode='hint') select explicit structured fields.
mode='protect_value' protects the whole selected scalar; hint requires a context-capable detector.
The JSON, DataFrame and RAG APIs accept schema= and context=; see adapters.
Detection and findings_v2 contain canonical Unicode code-point positions, source ranges, detector identity and decisions.
Positions are zero-based and end-exclusive. Source ranges refer to the supplied string, never PDF bytes.
mapping_quality is exact, covering or unavailable.
counts_v2 separates detector hits from resolved replacements.
coverage_v2 reports coverage bounds; uncertain coverage leaves pii_chars and pii_share unset.
provenance_v2 records configured detector and policy identities.
include_entities=True explicitly adds sensitive findings_v2_values.
Reversible processing returns anon_map, or only anon_map_reference when a token store is supplied.
The three-value result unpacking contract stays unchanged.
Register external detectors through DetectorRegistry.register(name, factory) and select them with DetectorSpec(name, options).
Nonempty detector_specs replaces the complete default detector set.
Factories and credentials belong to trusted application code. Requests cannot supply executable factory imports.
See configuration for network permission, source mapping and experimental routing.
Document, corpus and token-store APIs¶
| API | Result and boundary |
|---|---|
extract_document(path, provider=...) |
Validated ExtractedDocument; source files remain unchanged |
clean_document(document, cleaner) |
Cleaned text and structural elements; unified placeholder or synthetic output only |
analyse_records(..., cleaner=..., score_config=..., selection=..., scorer=None) |
Streaming analyses with private output references; scores receive original text |
select_records(analyses, selection=...) |
Context-managed report; close it to release owned temporary text and shingles |
curate_resumable(source, output, retention='caller_managed', ...) |
Private native checkpoints, checksum validation and replay; no exactly-once guarantee |
EncryptedSQLiteTokenStore(path, scope=..., key_provider=..., ttl_seconds=..., ...) |
Encrypted reversible maps behind opaque references; application-owned authorization and keys |
Corpus APIs live in sct.corpus; the encrypted store lives in sct.token_stores.
Document APIs are also exported from sct.
See documents, corpus integrations and token-store operations for their complete restrictions.
Batch processing¶
from sct import TextCleaner, TextCleanerConfig
cfg = TextCleanerConfig(
check_remove_stopwords=True,
check_remove_punctuation=True,
check_ner_process=True,
positional_tags=('PER', 'ORG', 'LOC'),
ner_confidence_threshold=0.90,
)
cleaner = TextCleaner(cfg=cfg)
# Sample texts
texts = [
"Email maria.garcia@example.es for more info.", # Spanish
"Besuchen Sie uns im Büro in Berlin.", # German
"Voor vragen, bel +31 20 123 4567.", # Dutch
]
# Process texts in batch (uses ThreadPoolExecutor for parallel processing)
results = cleaner.process_batch(texts, max_workers=2)
for lm_text, stat_text, lang in results:
print(f"Language: {lang}")
print(f"LM Format: {lm_text}")
print(f"Stat Format: {stat_text}")
print("-" * 40)