@privacyscrubber/sdk 2.3.7 → 2.4.0

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 (45) hide show
  1. package/README.md +86 -28
  2. package/benchmark.js +347 -0
  3. package/browser.js +2 -2
  4. package/cli.js +54 -2
  5. package/examples/python-bridge/MANIFEST.in +5 -0
  6. package/examples/python-bridge/README.md +265 -0
  7. package/examples/python-bridge/bridge_daemon.cjs +241 -0
  8. package/examples/python-bridge/dist/privacyscrubber-2.3.9-py3-none-any.whl +0 -0
  9. package/examples/python-bridge/dist/privacyscrubber-2.3.9.tar.gz +0 -0
  10. package/examples/python-bridge/fastapi_example.py +206 -0
  11. package/examples/python-bridge/langchain_adapter.py +20 -0
  12. package/examples/python-bridge/langchain_example.py +126 -0
  13. package/examples/python-bridge/llamaindex_example.py +103 -0
  14. package/examples/python-bridge/privacyscrubber/__init__.py +42 -0
  15. package/examples/python-bridge/privacyscrubber/bridge_daemon.cjs +241 -0
  16. package/examples/python-bridge/privacyscrubber/cli.py +138 -0
  17. package/examples/python-bridge/privacyscrubber/client.py +542 -0
  18. package/examples/python-bridge/privacyscrubber/exceptions.py +26 -0
  19. package/examples/python-bridge/privacyscrubber/langchain.py +161 -0
  20. package/examples/python-bridge/privacyscrubber/types.py +52 -0
  21. package/examples/python-bridge/privacyscrubber.egg-info/PKG-INFO +311 -0
  22. package/examples/python-bridge/privacyscrubber.egg-info/SOURCES.txt +18 -0
  23. package/examples/python-bridge/privacyscrubber.egg-info/dependency_links.txt +1 -0
  24. package/examples/python-bridge/privacyscrubber.egg-info/entry_points.txt +3 -0
  25. package/examples/python-bridge/privacyscrubber.egg-info/requires.txt +18 -0
  26. package/examples/python-bridge/privacyscrubber.egg-info/top_level.txt +1 -0
  27. package/examples/python-bridge/privacyscrubber.py +36 -0
  28. package/examples/python-bridge/pyproject.toml +62 -0
  29. package/examples/python-bridge/setup.py +66 -0
  30. package/examples/python-bridge/test_python_bridge.py +320 -0
  31. package/examples/vercel-ai-stream.js +195 -0
  32. package/fastmcp.cjs +291 -2
  33. package/fastmcp.d.ts +18 -0
  34. package/fastmcp.js +4 -0
  35. package/index.cjs +211 -134
  36. package/index.js +211 -134
  37. package/package.json +15 -3
  38. package/ps-license-manager.js +1 -1
  39. package/ps-pii-engine.cjs +1 -1
  40. package/scrubber-core.cjs +1 -1
  41. package/vercel-ai.cjs +400 -0
  42. package/vercel-ai.d.ts +156 -0
  43. package/vercel-ai.js +36 -0
  44. package/vision.cjs +13 -5
  45. package/vision.js +13 -5
@@ -0,0 +1,206 @@
1
+ """
2
+ PrivacyScrubber ZTDS - FastAPI Zero-Egress Proxy & Microservice Example
3
+
4
+ Demonstrates:
5
+ 1. Lifespan event management with AsyncPrivacyScrubber (zero-overhead async IPC).
6
+ 2. /v1/chat/completions OpenAI-compatible proxy endpoint:
7
+ - Sanitizes raw user prompts before cloud transmission (zero cleartext PII egress).
8
+ - Re-hydrates LLM answers with original PII in local RAM before responding to the user.
9
+ 3. Dedicated microservice endpoints: /api/v1/sanitize and /api/v1/restore.
10
+ 4. Direct runnable simulation if uvicorn/fastapi are not installed in the local environment.
11
+
12
+ Run:
13
+ python3 fastapi_example.py
14
+ # or: uvicorn fastapi_example:app --port 8000 --reload
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import asyncio
20
+ import json
21
+ import sys
22
+ from typing import Any, Dict, List, Optional
23
+
24
+ from privacyscrubber import AsyncPrivacyScrubber, SanitizeResult
25
+
26
+ try:
27
+ from fastapi import FastAPI, Depends, HTTPException, Header, Request, Response
28
+ from fastapi.responses import JSONResponse
29
+ from pydantic import BaseModel, Field
30
+ FASTAPI_AVAILABLE = True
31
+ except ImportError:
32
+ FASTAPI_AVAILABLE = False
33
+
34
+
35
+ # Pydantic Schemas (if FastAPI is available)
36
+ if FASTAPI_AVAILABLE:
37
+ class ChatMessage(BaseModel):
38
+ role: str
39
+ content: str
40
+
41
+ class ChatCompletionRequest(BaseModel):
42
+ model: str = "gpt-4o"
43
+ messages: List[ChatMessage]
44
+ temperature: Optional[float] = 0.7
45
+ profile: Optional[str] = "General"
46
+
47
+ class SanitizeRequest(BaseModel):
48
+ text: str
49
+ profile: Optional[str] = "General"
50
+ detect_secrets: Optional[bool] = False
51
+
52
+ class RestoreRequest(BaseModel):
53
+ text: str
54
+ token_map: Optional[Dict[str, str]] = None
55
+
56
+ # Global Scrubber Instance
57
+ scrubber = AsyncPrivacyScrubber(default_profile="General")
58
+
59
+ app = FastAPI(
60
+ title="PrivacyScrubber ZTDS Sidecar Proxy",
61
+ description="Zero-Trust Data Sanitization & In-Memory PII Shield for LLM Pipelines",
62
+ version="2.4.0"
63
+ )
64
+
65
+ @app.on_event("startup")
66
+ async def startup_event():
67
+ await scrubber.start()
68
+ print("[ZTDS Sidecar] PrivacyScrubber async sidecar initialized.")
69
+
70
+ @app.on_event("shutdown")
71
+ async def shutdown_event():
72
+ await scrubber.close()
73
+ print("[ZTDS Sidecar] PrivacyScrubber async sidecar closed.")
74
+
75
+ @app.get("/health")
76
+ async def health_check():
77
+ status = await scrubber.status()
78
+ ping = await scrubber.ping()
79
+ return {
80
+ "status": "healthy",
81
+ "engine": ping.get("engine"),
82
+ "version": ping.get("version"),
83
+ "license_tier": status.get("license_tier"),
84
+ "memory": status.get("memory_usage")
85
+ }
86
+
87
+ @app.post("/v1/chat/completions")
88
+ async def chat_completions_proxy(
89
+ req: ChatCompletionRequest,
90
+ response: Response
91
+ ):
92
+ """
93
+ OpenAI-compatible chat completion proxy:
94
+ 1. Intercepts incoming messages and sanitizes PII in client RAM (<0.5ms).
95
+ 2. Simulates (or forwards) transmission of tokenized prompt to cloud LLM.
96
+ 3. Restores tokens in the model response before returning to authorized client.
97
+ """
98
+ session_token_map: Dict[str, str] = {}
99
+ scrubbed_messages: List[Dict[str, str]] = []
100
+
101
+ # 1. Sanitize incoming conversation messages
102
+ for msg in req.messages:
103
+ if msg.role in ("user", "system"):
104
+ res = await scrubber.sanitize(msg.content, profile=req.profile)
105
+ scrubbed_messages.append({"role": msg.role, "content": res.sanitized})
106
+ session_token_map.update(res.token_map)
107
+ else:
108
+ scrubbed_messages.append({"role": msg.role, "content": msg.content})
109
+
110
+ # 2. Simulated LLM call over tokenized text (Zero Cleartext PII Egress)
111
+ # In production, forward `scrubbed_messages` to httpx.post("https://api.openai.com/v1/chat/completions")
112
+ user_query = scrubbed_messages[-1]["content"] if scrubbed_messages else ""
113
+ simulated_llm_reply = (
114
+ f"Understood. I have recorded your intake for {user_query}. "
115
+ f"Verification receipt confirmed for compliance audit."
116
+ )
117
+
118
+ # 3. Restore cleartext tokens in RAM before sending back to user
119
+ restored = await scrubber.restore(simulated_llm_reply, token_map=session_token_map)
120
+
121
+ response.headers["X-ZTDS-Status"] = "ENFORCED"
122
+ response.headers["X-ZTDS-Tokens-Masked"] = str(len(session_token_map))
123
+
124
+ return {
125
+ "id": "chatcmpl-ztds-simulated",
126
+ "object": "chat.completion",
127
+ "model": req.model,
128
+ "choices": [
129
+ {
130
+ "index": 0,
131
+ "message": {
132
+ "role": "assistant",
133
+ "content": restored.restored_text
134
+ },
135
+ "finish_reason": "stop"
136
+ }
137
+ ],
138
+ "ztds_meta": {
139
+ "tokens_masked": len(session_token_map),
140
+ "zero_egress_verified": True
141
+ }
142
+ }
143
+
144
+ @app.post("/api/v1/sanitize")
145
+ async def sanitize_endpoint(req: SanitizeRequest):
146
+ res = await scrubber.sanitize(
147
+ req.text,
148
+ profile=req.profile,
149
+ detect_secrets=req.detect_secrets
150
+ )
151
+ return {
152
+ "sanitized": res.sanitized,
153
+ "token_map": res.token_map,
154
+ "count": res.count,
155
+ "execution_ms": res.execution_ms,
156
+ "telemetry": res.telemetry,
157
+ "audit_receipt": res.audit_receipt
158
+ }
159
+
160
+ @app.post("/api/v1/restore")
161
+ async def restore_endpoint(req: RestoreRequest):
162
+ res = await scrubber.restore(req.text, token_map=req.token_map)
163
+ return {
164
+ "restored_text": res.restored_text,
165
+ "restored_count": res.restored_count
166
+ }
167
+
168
+
169
+ # Direct executable simulation runner
170
+ async def run_simulation():
171
+ print("=== PrivacyScrubber ZTDS: FastAPI Zero-Egress Proxy Simulation ===\n")
172
+ async with AsyncPrivacyScrubber(default_profile="Healthcare") as ps:
173
+ # 1. Health check
174
+ ping = await ps.ping()
175
+ print(f"1. Connected to ZTDS Engine: {ping.get('engine')} v{ping.get('version')}")
176
+
177
+ # 2. Incoming sensitive user payload
178
+ raw_user_prompt = "Escalation for Patient Sarah Connor (DOB: 1984-05-12, SSN: 000-11-2222, email: sconnor@cyberdyne.org) complaining of acute migraines."
179
+ print(f"\n2. Incoming Request Payload (Local Client RAM):\n \"{raw_user_prompt}\"")
180
+
181
+ # 3. Pre-LLM Sanitization
182
+ sanitize_result = await ps.sanitize(raw_user_prompt, profile="Healthcare")
183
+ print(f"\n3. Sanitized Prompt for Cloud LLM Transmission (Zero PII Egress):\n \"{sanitize_result.sanitized}\"")
184
+ print(f" Tokens Masked: {sanitize_result.count} | Latency: {sanitize_result.execution_ms:.2f}ms")
185
+ print(f" Token Map (RAM Only): {json.dumps(sanitize_result.token_map)}")
186
+
187
+ # 4. Cloud LLM synthesizes response operating ONLY on surrogate tokens
188
+ simulated_cloud_ai_response = (
189
+ f"Based on clinical records for [NAME_1] (ID [ID_1], contact [EMAIL_1]), "
190
+ f"recommend standard migraine protocol and follow-up consultation."
191
+ )
192
+ print(f"\n4. Raw Cloud LLM Output (Contains Only Tokens):\n \"{simulated_cloud_ai_response}\"")
193
+
194
+ # 5. Post-LLM In-Memory Token Restoration
195
+ restored = await ps.restore(simulated_cloud_ai_response, token_map=sanitize_result.token_map)
196
+ print(f"\n5. Restored Response for Authorized User (Local RAM Only):\n \"{restored.restored_text}\"")
197
+
198
+ print("\n=== Simulation Complete: 0 Bytes PII Transmitted to Cloud LLM ===")
199
+
200
+
201
+ if __name__ == "__main__":
202
+ if FASTAPI_AVAILABLE and "uvicorn" in sys.modules:
203
+ import uvicorn
204
+ uvicorn.run(app, host="0.0.0.0", port=8000)
205
+ else:
206
+ asyncio.run(run_simulation())
@@ -0,0 +1,20 @@
1
+ """
2
+ PrivacyScrubber ZTDS Adapter for LangChain
3
+ Zero-Trust Data Sanitization & Local PII Shield for LangChain Pipelines.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from privacyscrubber.langchain import (
9
+ ZTDSCallbackHandler,
10
+ ZTDSDocumentTransformer,
11
+ create_ztds_lcel_runnables,
12
+ LANGCHAIN_INSTALLED,
13
+ )
14
+
15
+ __all__ = [
16
+ "ZTDSCallbackHandler",
17
+ "ZTDSDocumentTransformer",
18
+ "create_ztds_lcel_runnables",
19
+ "LANGCHAIN_INSTALLED",
20
+ ]
@@ -0,0 +1,126 @@
1
+ """
2
+ PrivacyScrubber ZTDS - LangChain RAG & Callback Handler Example
3
+
4
+ Demonstrates:
5
+ 1. ZTDSCallbackHandler: Automatic pre-LLM prompt tokenization and post-LLM response restoration.
6
+ 2. ZTDSDocumentTransformer: Sanitizing document page_content and metadata before vector embedding.
7
+ 3. LCEL (LangChain Expression Language) pipeline with create_ztds_lcel_runnables().
8
+ 4. Operates 100% in local memory with zero cloud network leakage.
9
+
10
+ Run:
11
+ python3 langchain_example.py
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ from privacyscrubber import PrivacyScrubberClient
18
+ from langchain_adapter import (
19
+ ZTDSCallbackHandler,
20
+ ZTDSDocumentTransformer,
21
+ create_ztds_lcel_runnables,
22
+ LANGCHAIN_INSTALLED
23
+ )
24
+
25
+
26
+ def run_document_transformer_demo(client: PrivacyScrubberClient):
27
+ print("--- 1. Vector RAG Document Pre-Ingestion Guard ---")
28
+
29
+ class SimpleDoc:
30
+ def __init__(self, page_content: str, metadata: dict):
31
+ self.page_content = page_content
32
+ self.metadata = metadata
33
+
34
+ raw_docs = [
35
+ SimpleDoc(
36
+ page_content="Lead Account Executive Bob Vance (bob.vance@vancerefrigeration.com, phone 570-555-0143) closed contract under EIN 12-3456789.",
37
+ metadata={"doc_id": "contract_901", "author": "bob.vance@vancerefrigeration.com", "source": "HubSpot"}
38
+ ),
39
+ SimpleDoc(
40
+ page_content="Customer escalation from Alice Morgan (amorgan@investcorp.com): wire transfer of $250,000 sent to IBAN GB29XAAA14151617181920.",
41
+ metadata={"doc_id": "wire_902", "author": "amorgan@investcorp.com", "source": "Zendesk"}
42
+ )
43
+ ]
44
+
45
+ transformer = ZTDSDocumentTransformer(
46
+ scrubber=client,
47
+ profile="Financial",
48
+ sensitive_metadata_keys=["author"]
49
+ )
50
+
51
+ sanitized_docs = transformer.transform_documents(raw_docs)
52
+
53
+ print("Sanitized Documents (Ready for Vector Embeddings):")
54
+ for doc in sanitized_docs:
55
+ print(f" [ID: {doc.metadata['doc_id']}]")
56
+ print(f" Content: {doc.page_content}")
57
+ print(f" Metadata: {doc.metadata}\n")
58
+
59
+
60
+ def run_callback_handler_demo(client: PrivacyScrubberClient):
61
+ print("--- 2. ZTDS Callback Handler (Pre/Post LLM Interceptor) ---")
62
+
63
+ handler = ZTDSCallbackHandler(scrubber=client, profile="Financial", verbose=True)
64
+
65
+ # Simulated LangChain run:
66
+ # 1. Outgoing prompt before transmission
67
+ test_prompt = ["Analyze fraud risk for customer David Miller (SSN 987-65-4321, account # 4400-1122-3344-5566)."]
68
+ print(f"Prompt Before Interception:\n \"{test_prompt[0]}\"")
69
+
70
+ handler.on_llm_start(serialized={}, prompts=test_prompt, run_id="run_abc123")
71
+ print(f"Prompt Sent to Cloud LLM:\n \"{test_prompt[0]}\"")
72
+
73
+ # 2. Simulated LLM answer generated over tokens
74
+ class MockGeneration:
75
+ def __init__(self, text):
76
+ self.text = text
77
+
78
+ class MockLLMResult:
79
+ def __init__(self, text):
80
+ self.generations = [[MockGeneration(text)]]
81
+
82
+ simulated_cloud_ai_reply = (
83
+ "Fraud analysis for customer [NAME_1] (Tax ID [ID_1], account [ID_2]): "
84
+ "No anomalous velocity detected over the past 30 days."
85
+ )
86
+ mock_result = MockLLMResult(simulated_cloud_ai_reply)
87
+
88
+ print(f"\nRaw Cloud LLM Output:\n \"{mock_result.generations[0][0].text}\"")
89
+
90
+ # 3. Intercept and restore cleartext in RAM
91
+ handler.on_llm_end(mock_result, run_id="run_abc123")
92
+ print(f"Restored Output in Local RAM:\n \"{mock_result.generations[0][0].text}\"\n")
93
+
94
+
95
+ def run_lcel_pipeline_demo(client: PrivacyScrubberClient):
96
+ print("--- 3. LCEL Pipeline Integration ---")
97
+ scrub_fn, restore_fn = create_ztds_lcel_runnables(client, profile="General")
98
+
99
+ user_query = "Please generate an introduction email for John Smith at john.smith@acme.com."
100
+ print(f"Input: \"{user_query}\"")
101
+
102
+ # Step 1: Pre-scrub
103
+ sanitized_prompt = scrub_fn(user_query)
104
+ print(f"Sanitized: \"{sanitized_prompt}\"")
105
+
106
+ # Step 2: Simulated LLM
107
+ mock_ai_output = f"Subject: Welcome! Dear [NAME_1], we are thrilled to connect with you at [EMAIL_1]."
108
+ print(f"AI Output: \"{mock_ai_output}\"")
109
+
110
+ # Step 3: Post-restore
111
+ final_output = restore_fn(mock_ai_output)
112
+ print(f"Restored: \"{final_output}\"\n")
113
+
114
+
115
+ def main():
116
+ print("=== PrivacyScrubber ZTDS: LangChain Integration Suite ===\n")
117
+ with PrivacyScrubberClient(default_profile="Financial") as client:
118
+ run_document_transformer_demo(client)
119
+ run_callback_handler_demo(client)
120
+ run_lcel_pipeline_demo(client)
121
+
122
+ print("=== Zero Cleartext PII Egress Verified Across All Pipelines ===")
123
+
124
+
125
+ if __name__ == "__main__":
126
+ main()
@@ -0,0 +1,103 @@
1
+ """
2
+ PrivacyScrubber ZTDS - LlamaIndex Node & RAG Transformation Example
3
+
4
+ Demonstrates:
5
+ 1. Sanitizing text nodes before vector embedding (VectorStoreIndex).
6
+ 2. Preserving semantic search accuracy with deterministic surrogate tokens.
7
+ 3. In-memory response rehydration upon retrieval and answer synthesis.
8
+ 4. 100% in-memory RAM execution. Zero external egress.
9
+
10
+ Run:
11
+ python3 llamaindex_example.py
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import copy
17
+ from typing import Any, Dict, List, Optional
18
+ from privacyscrubber import PrivacyScrubberClient
19
+
20
+
21
+ class ZTDSNodeTransformer:
22
+ """
23
+ Transforms LlamaIndex TextNodes or Document chunks before vector database insertion.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ scrubber: Optional[PrivacyScrubberClient] = None,
29
+ profile: str = "General"
30
+ ) -> None:
31
+ self.scrubber = scrubber or PrivacyScrubberClient(default_profile=profile)
32
+ self.profile = profile
33
+
34
+ def transform_nodes(self, nodes: List[Any]) -> List[Any]:
35
+ """Sanitizes text and metadata in place or creates clean copies."""
36
+ transformed: List[Any] = []
37
+ for node in nodes:
38
+ node_copy = copy.deepcopy(node)
39
+ text = getattr(node_copy, "text", "")
40
+ if text and isinstance(text, str):
41
+ res = self.scrubber.sanitize(text, profile=self.profile)
42
+ node_copy.text = res.sanitized
43
+
44
+ metadata = getattr(node_copy, "metadata", None)
45
+ if isinstance(metadata, dict):
46
+ for k, v in list(metadata.items()):
47
+ if isinstance(v, str):
48
+ res_m = self.scrubber.sanitize(v, profile=self.profile)
49
+ metadata[k] = res_m.sanitized
50
+
51
+ transformed.append(node_copy)
52
+ return transformed
53
+
54
+
55
+ def main():
56
+ print("=== PrivacyScrubber ZTDS: LlamaIndex Ingestion Demo ===\n")
57
+
58
+ class MockTextNode:
59
+ def __init__(self, node_id: str, text: str, metadata: Dict[str, Any]):
60
+ self.node_id = node_id
61
+ self.text = text
62
+ self.metadata = metadata
63
+
64
+ raw_nodes = [
65
+ MockTextNode(
66
+ node_id="chunk_001",
67
+ text="Employee review for Marcus Brody. Personal mobile is +1-202-555-0199, email mbrody@state.gov.",
68
+ metadata={"department": "Public Relations", "reviewer": "Marcus Brody"}
69
+ ),
70
+ MockTextNode(
71
+ node_id="chunk_002",
72
+ text="Compensation plan approved under Tax ID 99-8877665 for total salary $185,000.",
73
+ metadata={"department": "Executive Payroll", "approver": "Finance Board"}
74
+ )
75
+ ]
76
+
77
+ with PrivacyScrubberClient(default_profile="HR") as client:
78
+ transformer = ZTDSNodeTransformer(scrubber=client, profile="HR")
79
+
80
+ print("1. Raw LlamaIndex Chunks:")
81
+ for n in raw_nodes:
82
+ print(f" [{n.node_id}] {n.text}")
83
+
84
+ sanitized_nodes = transformer.transform_nodes(raw_nodes)
85
+
86
+ print("\n2. Sanitized Chunks (Safe for Vector Store Ingestion):")
87
+ for n in sanitized_nodes:
88
+ print(f" [{n.node_id}] {n.text}")
89
+ print(f" Metadata: {n.metadata}")
90
+
91
+ # 3. Simulate Query Engine Synthesis
92
+ query_output = "Based on retrieved node [chunk_001], [NAME_1] ([EMAIL_1]) has verified contact info."
93
+ print(f"\n3. Query Engine Output (LLM generated over surrogate tokens):\n \"{query_output}\"")
94
+
95
+ # 4. In-Memory Restoration
96
+ restored = client.restore(query_output)
97
+ print(f"\n4. Final Answer Restored in Local RAM:\n \"{restored.restored_text}\"")
98
+
99
+ print("\n=== LlamaIndex RAG Pipeline Verification Complete ===")
100
+
101
+
102
+ if __name__ == "__main__":
103
+ main()
@@ -0,0 +1,42 @@
1
+ """
2
+ PrivacyScrubber - Zero-Trust Data Sanitization & In-Memory PII Shield for Python.
3
+
4
+ RFC v1.0 standard compliant.
5
+ 100% in-memory client-side RAM execution. Zero external network egress.
6
+ Sub-millisecond latency (<1ms).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from privacyscrubber.client import (
12
+ PrivacyScrubberClient,
13
+ PrivacyScrubber,
14
+ AsyncPrivacyScrubberClient,
15
+ AsyncPrivacyScrubber,
16
+ )
17
+ from privacyscrubber.types import (
18
+ SanitizeResult,
19
+ RestoreResult,
20
+ ObjectSanitizeResult,
21
+ )
22
+ from privacyscrubber.exceptions import (
23
+ PrivacyScrubberError,
24
+ PrivacyScrubberLicenseError,
25
+ )
26
+
27
+ __version__ = "2.4.0"
28
+ __author__ = "Ilya Sibiryakov (BrandMeWeb)"
29
+ __license__ = "Proprietary"
30
+
31
+ __all__ = [
32
+ "PrivacyScrubberClient",
33
+ "PrivacyScrubber",
34
+ "AsyncPrivacyScrubberClient",
35
+ "AsyncPrivacyScrubber",
36
+ "SanitizeResult",
37
+ "RestoreResult",
38
+ "ObjectSanitizeResult",
39
+ "PrivacyScrubberError",
40
+ "PrivacyScrubberLicenseError",
41
+ "__version__",
42
+ ]