@privacyscrubber/sdk 2.3.9 → 2.4.1

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 (37) hide show
  1. package/README.md +85 -27
  2. package/benchmark.js +355 -0
  3. package/browser.js +276 -39
  4. package/cli.js +1 -1
  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/fastapi_example.py +206 -0
  9. package/examples/python-bridge/langchain_adapter.py +20 -0
  10. package/examples/python-bridge/langchain_example.py +126 -0
  11. package/examples/python-bridge/llamaindex_example.py +103 -0
  12. package/examples/python-bridge/privacyscrubber/__init__.py +42 -0
  13. package/examples/python-bridge/privacyscrubber/bridge_daemon.cjs +241 -0
  14. package/examples/python-bridge/privacyscrubber/cli.py +138 -0
  15. package/examples/python-bridge/privacyscrubber/client.py +548 -0
  16. package/examples/python-bridge/privacyscrubber/exceptions.py +26 -0
  17. package/examples/python-bridge/privacyscrubber/langchain.py +161 -0
  18. package/examples/python-bridge/privacyscrubber/types.py +52 -0
  19. package/examples/python-bridge/privacyscrubber.py +36 -0
  20. package/examples/python-bridge/pyproject.toml +62 -0
  21. package/examples/python-bridge/setup.py +66 -0
  22. package/examples/python-bridge/test_python_bridge.py +320 -0
  23. package/examples/vercel-ai-stream.js +195 -0
  24. package/fastmcp.cjs +289 -8
  25. package/fastmcp.d.ts +18 -0
  26. package/fastmcp.js +4 -0
  27. package/index.cjs +99 -42
  28. package/index.js +103 -46
  29. package/package.json +15 -3
  30. package/ps-license-manager.js +1 -1
  31. package/ps-pii-engine.cjs +1 -1
  32. package/scrubber-core.cjs +1 -1
  33. package/vercel-ai.cjs +400 -0
  34. package/vercel-ai.d.ts +156 -0
  35. package/vercel-ai.js +36 -0
  36. package/vision.cjs +13 -5
  37. package/vision.js +13 -5
@@ -0,0 +1,161 @@
1
+ """
2
+ PrivacyScrubber LangChain Adapter
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import copy
8
+ from typing import Any, Dict, List, Optional, Sequence, Union
9
+
10
+ from privacyscrubber.client import PrivacyScrubberClient
11
+
12
+
13
+ try:
14
+ from langchain_core.callbacks.base import BaseCallbackHandler
15
+ from langchain_core.documents import Document
16
+ from langchain_core.outputs import LLMResult
17
+ from langchain_core.runnables import RunnableLambda
18
+ LANGCHAIN_INSTALLED = True
19
+ except ImportError:
20
+ LANGCHAIN_INSTALLED = False
21
+ BaseCallbackHandler = object # type: ignore
22
+ Document = object # type: ignore
23
+ LLMResult = object # type: ignore
24
+ RunnableLambda = object # type: ignore
25
+
26
+
27
+ class ZTDSCallbackHandler(BaseCallbackHandler):
28
+ """
29
+ LangChain Callback Handler enforcing Zero-Trust Data Sanitization.
30
+
31
+ 1. Intercepts outgoing prompts in on_llm_start -> replaces raw PII with surrogate tokens.
32
+ 2. Stores ephemeral token mappings in RAM only (no disk, no network).
33
+ 3. Intercepts incoming responses in on_llm_end -> restores cleartext values in RAM.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ scrubber: Optional[PrivacyScrubberClient] = None,
39
+ profile: str = "General",
40
+ detect_secrets: bool = False,
41
+ verbose: bool = False
42
+ ) -> None:
43
+ self.scrubber = scrubber or PrivacyScrubberClient(default_profile=profile)
44
+ self.profile = profile
45
+ self.detect_secrets = detect_secrets
46
+ self.verbose = verbose
47
+ self._session_token_maps: Dict[str, Dict[str, str]] = {}
48
+
49
+ def on_llm_start(
50
+ self,
51
+ serialized: Dict[str, Any],
52
+ prompts: List[str],
53
+ *,
54
+ run_id: Optional[Any] = None,
55
+ parent_run_id: Optional[Any] = None,
56
+ tags: Optional[List[str]] = None,
57
+ metadata: Optional[Dict[str, Any]] = None,
58
+ **kwargs: Any
59
+ ) -> Any:
60
+ """Sanitizes prompts in place before they are dispatched to the cloud LLM."""
61
+ key = str(run_id) if run_id else "default"
62
+ combined_token_map: Dict[str, str] = {}
63
+
64
+ for i, prompt in enumerate(prompts):
65
+ res = self.scrubber.sanitize(
66
+ prompt,
67
+ profile=self.profile,
68
+ detect_secrets=self.detect_secrets,
69
+ persist_session=True
70
+ )
71
+ prompts[i] = res.sanitized
72
+ combined_token_map.update(res.token_map)
73
+ if self.verbose and res.count > 0:
74
+ print(f"[ZTDS Guard] Sanitized {res.count} PII tokens for run {key}")
75
+
76
+ self._session_token_maps[key] = combined_token_map
77
+
78
+ def on_llm_end(
79
+ self,
80
+ response: Any,
81
+ *,
82
+ run_id: Optional[Any] = None,
83
+ parent_run_id: Optional[Any] = None,
84
+ tags: Optional[List[str]] = None,
85
+ **kwargs: Any
86
+ ) -> Any:
87
+ """Restores cleartext tokens in LLM output before reaching the user."""
88
+ key = str(run_id) if run_id else "default"
89
+ token_map = self._session_token_maps.pop(key, None)
90
+
91
+ generations = getattr(response, "generations", None)
92
+ if generations and isinstance(generations, list):
93
+ for gen_list in generations:
94
+ for gen in gen_list:
95
+ if hasattr(gen, "text") and gen.text:
96
+ restored = self.scrubber.restore(gen.text, token_map=token_map)
97
+ gen.text = restored.restored_text
98
+ elif hasattr(gen, "message") and hasattr(gen.message, "content"):
99
+ if isinstance(gen.message.content, str):
100
+ restored = self.scrubber.restore(gen.message.content, token_map=token_map)
101
+ gen.message.content = restored.restored_text
102
+
103
+ return response
104
+
105
+
106
+ class ZTDSDocumentTransformer:
107
+ """
108
+ Sanitizes LangChain Documents before vector database ingestion (Pinecone, Qdrant, Chroma).
109
+ Guarantees no raw PII enters embeddings or persistent vector storage.
110
+ """
111
+
112
+ def __init__(
113
+ self,
114
+ scrubber: Optional[PrivacyScrubberClient] = None,
115
+ profile: str = "General",
116
+ sensitive_metadata_keys: Optional[Sequence[str]] = None
117
+ ) -> None:
118
+ self.scrubber = scrubber or PrivacyScrubberClient(default_profile=profile)
119
+ self.profile = profile
120
+ self.sensitive_metadata_keys = list(sensitive_metadata_keys or [])
121
+
122
+ def transform_documents(self, documents: Sequence[Any]) -> List[Any]:
123
+ """Transforms documents, sanitizing page_content and sensitive metadata keys."""
124
+ transformed: List[Any] = []
125
+ for doc in documents:
126
+ doc_copy = copy.deepcopy(doc)
127
+ content = getattr(doc_copy, "page_content", "")
128
+ if isinstance(content, str) and content:
129
+ res = self.scrubber.sanitize(content, profile=self.profile)
130
+ doc_copy.page_content = res.sanitized
131
+
132
+ metadata = getattr(doc_copy, "metadata", None)
133
+ if isinstance(metadata, dict):
134
+ for k in self.sensitive_metadata_keys:
135
+ if k in metadata and isinstance(metadata[k], str):
136
+ res_meta = self.scrubber.sanitize(metadata[k], profile=self.profile)
137
+ metadata[k] = res_meta.sanitized
138
+
139
+ transformed.append(doc_copy)
140
+ return transformed
141
+
142
+
143
+ def create_ztds_lcel_runnables(
144
+ scrubber: Optional[PrivacyScrubberClient] = None,
145
+ profile: str = "General"
146
+ ):
147
+ """
148
+ Creates pre-scrub and post-restore runnables for LCEL pipelines:
149
+ chain = scrub_runnable | prompt | model | parser | restore_runnable
150
+ """
151
+ client = scrubber or PrivacyScrubberClient(default_profile=profile)
152
+
153
+ def scrub_func(input_text: str) -> str:
154
+ res = client.sanitize(str(input_text), profile=profile)
155
+ return res.sanitized
156
+
157
+ def restore_func(output_text: str) -> str:
158
+ restored = client.restore(str(output_text))
159
+ return restored.restored_text
160
+
161
+ return scrub_func, restore_func
@@ -0,0 +1,52 @@
1
+ """
2
+ PrivacyScrubber Data Types & Response Models
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any, Dict, List, Optional
9
+
10
+
11
+ @dataclass
12
+ class SanitizeResult:
13
+ """Result of a text sanitization operation."""
14
+ sanitized: str
15
+ token_map: Dict[str, str]
16
+ count: int = 0
17
+ execution_ms: float = 0.0
18
+ telemetry: Optional[Dict[str, Any]] = None
19
+ audit_receipt: Optional[str] = None
20
+ license: Optional[Dict[str, Any]] = None
21
+ matches: Optional[List[Dict[str, Any]]] = None
22
+
23
+ def __str__(self) -> str:
24
+ """Allow direct string usage in prompts and string formatting."""
25
+ return self.sanitized
26
+
27
+ @property
28
+ def scrubbed_text(self) -> str:
29
+ """Alias for sanitized text."""
30
+ return self.sanitized
31
+
32
+
33
+ @dataclass
34
+ class RestoreResult:
35
+ """Result of a token restoration operation."""
36
+ restored_text: str
37
+ restored_count: int = 0
38
+
39
+ def __str__(self) -> str:
40
+ """Allow direct string usage in final responses."""
41
+ return self.restored_text
42
+
43
+
44
+ @dataclass
45
+ class ObjectSanitizeResult:
46
+ """Result of a structured dictionary or list sanitization operation."""
47
+ sanitized: Any
48
+ token_map: Dict[str, str]
49
+ count: int = 0
50
+ execution_ms: float = 0.0
51
+ telemetry: Optional[Dict[str, Any]] = None
52
+ audit_receipt: Optional[str] = None
@@ -0,0 +1,36 @@
1
+ """
2
+ PrivacyScrubber ZTDS Client for Python
3
+ Zero-Trust Data Sanitization & Local PII Shield for LLM / AI Pipelines.
4
+
5
+ 100% in-memory client-side execution. Zero cloud egress. Sub-millisecond latency.
6
+ RFC v1.0 standard-compliant Zero-Trust Data Sanitization engine.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from privacyscrubber import (
12
+ PrivacyScrubberClient,
13
+ PrivacyScrubber,
14
+ AsyncPrivacyScrubberClient,
15
+ AsyncPrivacyScrubber,
16
+ SanitizeResult,
17
+ RestoreResult,
18
+ ObjectSanitizeResult,
19
+ PrivacyScrubberError,
20
+ PrivacyScrubberLicenseError,
21
+ __version__,
22
+ )
23
+ from privacyscrubber.client import _find_daemon_path
24
+
25
+ __all__ = [
26
+ "PrivacyScrubberClient",
27
+ "PrivacyScrubber",
28
+ "AsyncPrivacyScrubberClient",
29
+ "AsyncPrivacyScrubber",
30
+ "SanitizeResult",
31
+ "RestoreResult",
32
+ "ObjectSanitizeResult",
33
+ "PrivacyScrubberError",
34
+ "PrivacyScrubberLicenseError",
35
+ "__version__",
36
+ ]
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "privacyscrubber"
7
+ version = "2.4.1"
8
+ description = "Zero-Trust Data Sanitization & In-Memory PII Shield for Python AI Pipelines (ZTDS RFC v1.0)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "Commercial / Proprietary" }
12
+ authors = [
13
+ { name = "Ilya Sibiryakov (BrandMeWeb)", email = "contact@privacyscrubber.com" }
14
+ ]
15
+ keywords = [
16
+ "pii", "redaction", "sanitization", "zero-trust", "ztds", "langchain",
17
+ "fastapi", "presidio", "llm-security", "gdpr", "hipaa", "ai-guardrail"
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 5 - Production/Stable",
21
+ "Intended Audience :: Developers",
22
+ "Intended Audience :: Information Technology",
23
+ "Topic :: Security",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.8",
27
+ "Programming Language :: Python :: 3.9",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Operating System :: OS Independent",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://privacyscrubber.com"
37
+ Documentation = "https://privacyscrubber.com/sdk/"
38
+ Repository = "https://github.com/moxno/privacyscrubber-mcp"
39
+ Standard = "https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/"
40
+
41
+ [project.optional-dependencies]
42
+ fastapi = ["fastapi>=0.100.0", "uvicorn>=0.20.0", "pydantic>=2.0.0"]
43
+ langchain = ["langchain-core>=0.1.0"]
44
+ llamaindex = ["llama-index-core>=0.10.0"]
45
+ all = [
46
+ "fastapi>=0.100.0",
47
+ "uvicorn>=0.20.0",
48
+ "pydantic>=2.0.0",
49
+ "langchain-core>=0.1.0",
50
+ "llama-index-core>=0.10.0"
51
+ ]
52
+
53
+ [project.scripts]
54
+ privacyscrubber = "privacyscrubber.cli:main"
55
+ ps-bridge = "privacyscrubber.cli:main"
56
+
57
+ [tool.setuptools.packages.find]
58
+ where = ["."]
59
+ include = ["privacyscrubber*"]
60
+
61
+ [tool.setuptools.package-data]
62
+ privacyscrubber = ["*.cjs", "bridge_daemon.cjs"]
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Setup script for privacyscrubber Python package.
4
+ """
5
+
6
+ from setuptools import setup, find_packages
7
+ import os
8
+
9
+ here = os.path.abspath(os.path.dirname(__file__))
10
+
11
+ with open(os.path.join(here, "README.md"), encoding="utf-8") as f:
12
+ long_description = f.read()
13
+
14
+ setup(
15
+ name="privacyscrubber",
16
+ version="2.4.1",
17
+ description="Zero-Trust Data Sanitization & In-Memory PII Shield for Python AI Pipelines (ZTDS RFC v1.0)",
18
+ long_description=long_description,
19
+ long_description_content_type="text/markdown",
20
+ author="Ilya Sibiryakov (BrandMeWeb)",
21
+ author_email="contact@privacyscrubber.com",
22
+ url="https://privacyscrubber.com",
23
+ project_urls={
24
+ "Documentation": "https://privacyscrubber.com/sdk/",
25
+ "Source": "https://github.com/moxno/privacyscrubber-mcp",
26
+ "ZTDS Standard": "https://datatracker.ietf.org/doc/draft-sibiryakov-ztds-protocol/",
27
+ },
28
+ packages=find_packages(),
29
+ package_data={
30
+ "privacyscrubber": ["*.cjs", "bridge_daemon.cjs"],
31
+ },
32
+ include_package_data=True,
33
+ python_requires=">=3.8",
34
+ install_requires=[],
35
+ extras_require={
36
+ "fastapi": ["fastapi>=0.100.0", "uvicorn>=0.20.0", "pydantic>=2.0.0"],
37
+ "langchain": ["langchain-core>=0.1.0"],
38
+ "llamaindex": ["llama-index-core>=0.10.0"],
39
+ "all": [
40
+ "fastapi>=0.100.0",
41
+ "uvicorn>=0.20.0",
42
+ "pydantic>=2.0.0",
43
+ "langchain-core>=0.1.0",
44
+ "llama-index-core>=0.10.0",
45
+ ],
46
+ },
47
+ entry_points={
48
+ "console_scripts": [
49
+ "privacyscrubber=privacyscrubber.cli:main",
50
+ "ps-bridge=privacyscrubber.cli:main",
51
+ ],
52
+ },
53
+ classifiers=[
54
+ "Development Status :: 5 - Production/Stable",
55
+ "Intended Audience :: Developers",
56
+ "Topic :: Security",
57
+ "Programming Language :: Python :: 3",
58
+ "Programming Language :: Python :: 3.8",
59
+ "Programming Language :: Python :: 3.9",
60
+ "Programming Language :: Python :: 3.10",
61
+ "Programming Language :: Python :: 3.11",
62
+ "Programming Language :: Python :: 3.12",
63
+ "Programming Language :: Python :: 3.13",
64
+ "Operating System :: OS Independent",
65
+ ],
66
+ )