trusys 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 (48) hide show
  1. trusys-0.1.0/PKG-INFO +330 -0
  2. trusys-0.1.0/README.md +303 -0
  3. trusys-0.1.0/pyproject.toml +57 -0
  4. trusys-0.1.0/setup.cfg +4 -0
  5. trusys-0.1.0/tests/test_actions.py +679 -0
  6. trusys-0.1.0/tests/test_execution.py +134 -0
  7. trusys-0.1.0/tests/test_guard.py +321 -0
  8. trusys-0.1.0/tests/test_pii_scanner.py +310 -0
  9. trusys-0.1.0/tests/test_regex_scanner.py +236 -0
  10. trusys-0.1.0/tests/test_scanners.py +133 -0
  11. trusys-0.1.0/tests/test_types.py +197 -0
  12. trusys-0.1.0/trusys/__init__.py +82 -0
  13. trusys-0.1.0/trusys/actions/__init__.py +28 -0
  14. trusys-0.1.0/trusys/actions/base.py +40 -0
  15. trusys-0.1.0/trusys/actions/builtin.py +608 -0
  16. trusys-0.1.0/trusys/actions/resolver.py +155 -0
  17. trusys-0.1.0/trusys/core/__init__.py +6 -0
  18. trusys-0.1.0/trusys/core/config.py +54 -0
  19. trusys-0.1.0/trusys/core/guard.py +869 -0
  20. trusys-0.1.0/trusys/exceptions.py +117 -0
  21. trusys-0.1.0/trusys/execution/__init__.py +19 -0
  22. trusys-0.1.0/trusys/execution/runner.py +739 -0
  23. trusys-0.1.0/trusys/execution/strategies.py +424 -0
  24. trusys-0.1.0/trusys/results/__init__.py +7 -0
  25. trusys-0.1.0/trusys/results/aggregator.py +118 -0
  26. trusys-0.1.0/trusys/results/formatter.py +151 -0
  27. trusys-0.1.0/trusys/results/uploader.py +294 -0
  28. trusys-0.1.0/trusys/scanners/__init__.py +6 -0
  29. trusys-0.1.0/trusys/scanners/base.py +143 -0
  30. trusys-0.1.0/trusys/scanners/builtin/__init__.py +23 -0
  31. trusys-0.1.0/trusys/scanners/builtin/local/__init__.py +7 -0
  32. trusys-0.1.0/trusys/scanners/builtin/local/blocklist.py +352 -0
  33. trusys-0.1.0/trusys/scanners/builtin/local/pii.py +378 -0
  34. trusys-0.1.0/trusys/scanners/builtin/local/regex.py +277 -0
  35. trusys-0.1.0/trusys/scanners/builtin/remote/__init__.py +6 -0
  36. trusys-0.1.0/trusys/scanners/builtin/remote/content_safety.py +58 -0
  37. trusys-0.1.0/trusys/scanners/builtin/remote/prompt_injection.py +54 -0
  38. trusys-0.1.0/trusys/scanners/registry.py +229 -0
  39. trusys-0.1.0/trusys/scanners/remote.py +543 -0
  40. trusys-0.1.0/trusys/types.py +169 -0
  41. trusys-0.1.0/trusys/utils/__init__.py +5 -0
  42. trusys-0.1.0/trusys/utils/helpers.py +94 -0
  43. trusys-0.1.0/trusys/utils/logging.py +222 -0
  44. trusys-0.1.0/trusys.egg-info/PKG-INFO +330 -0
  45. trusys-0.1.0/trusys.egg-info/SOURCES.txt +46 -0
  46. trusys-0.1.0/trusys.egg-info/dependency_links.txt +1 -0
  47. trusys-0.1.0/trusys.egg-info/requires.txt +14 -0
  48. trusys-0.1.0/trusys.egg-info/top_level.txt +1 -0
trusys-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.4
2
+ Name: trusys
3
+ Version: 0.1.0
4
+ Summary: A modular Python library for AI guardrails with input/output scanning capabilities
5
+ License-Expression: MIT
6
+ Keywords: ai,guardrails,llm,safety,scanning
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: fuzzysearch>=0.7.0
16
+ Requires-Dist: httpx>=0.27.0
17
+ Requires-Dist: pyffx>=0.3.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7.0; extra == "dev"
20
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
21
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
22
+ Requires-Dist: mypy>=1.0; extra == "dev"
23
+ Requires-Dist: ruff>=0.1; extra == "dev"
24
+ Provides-Extra: pii
25
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == "pii"
26
+ Requires-Dist: spacy>=3.0.0; extra == "pii"
27
+
28
+ # TruGuard
29
+
30
+ A Python library for AI guardrails. Configure your guardrails in the **Trusys portal**; in your application you run **`truguard init`** (or set credentials), call **`TruGuard.init()`**, then use the built-in **input** and **output** scanners. No manual scanner configuration in code — everything is managed from [console.trusys.ai](https://console.trusys.ai).
31
+
32
+ ## Features
33
+
34
+ - **Portal-driven configuration**: Define input and output guardrails in the [Trusys Console](https://console.trusys.ai); your app receives config automatically.
35
+ - **Static input and output guards**: Use `TruGuard.input_guard` and `TruGuard.output_guard` to scan prompts and model responses.
36
+ - **No code-level scanner setup**: Scanners (regex, blocklist, PII, content safety, etc.) are configured in the portal, not in your codebase.
37
+ - **Parallel execution**: Scanners run concurrently for better performance.
38
+ - **Flexible actions**: Fix, mask, filter, fail, or ignore violations based on portal settings.
39
+ - **Async support**: Full async/await for I/O-bound scanning.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install trusys
45
+ ```
46
+
47
+ ### Optional Dependencies
48
+
49
+ For PII (Personally Identifiable Information) detection when enabled in the portal:
50
+
51
+ ```bash
52
+ pip install trusys[pii]
53
+ python -m spacy download en_core_web_lg
54
+ ```
55
+
56
+ ## Quick Start
57
+
58
+ ### 1. Configure guardrails in the Trusys portal
59
+
60
+ 1. Go to **[https://console.trusys.ai](https://console.trusys.ai)** and sign in.
61
+ 2. Create or select an application.
62
+ 3. Configure **input** and **output** guardrails (e.g. blocklists, PII, content safety, prompt injection).
63
+ 4. Copy your **Application ID** and **API key** for the next step.
64
+
65
+ ### 2. Initialize TruGuard in your project
66
+
67
+ Configure your Application ID and API key by setting these environment variables:
68
+
69
+ - `TRUSYS_APPLICATION_ID` – from the Trusys portal
70
+ - `TRUSYS_API_KEY` – from the Trusys portal
71
+
72
+ ### 3. Use the input and output guards in code
73
+
74
+ Call `TruGuard.init()` once at application startup (e.g. in `main` or when your app loads). Then use the static **input** and **output** guards to scan content.
75
+
76
+ ```python
77
+ from trusys import TruGuard
78
+
79
+ # One-time init at startup (uses TRUSYS_APPLICATION_ID and TRUSYS_API_KEY)
80
+ TruGuard.init()
81
+
82
+ # Scan user input (e.g. before sending to the LLM)
83
+ input_result = TruGuard.input_guard.scan(content={"prompt": "Hello world!"})
84
+ if not input_result.passed:
85
+ # Handle violations (e.g. block request or apply fixes)
86
+ for v in input_result.all_violations:
87
+ print(f"Input violation: {v.message}")
88
+ raise ValueError("Input failed guardrails")
89
+
90
+ # ... call your LLM ...
91
+
92
+ # Scan model output (e.g. before returning to the user)
93
+ output_result = TruGuard.output_guard.scan(
94
+ content={"prompt": "user prompt", "response": "model response"}
95
+ )
96
+ if not output_result.passed:
97
+ for v in output_result.all_violations:
98
+ print(f"Output violation: {v.message}")
99
+ raise ValueError("Output failed guardrails")
100
+
101
+ # Use output_result.final_content for the (possibly fixed) response if needed
102
+ ```
103
+
104
+ ### Async usage
105
+
106
+ ```python
107
+ import asyncio
108
+ from trusys import TruGuard
109
+
110
+ TruGuard.init()
111
+
112
+ async def main():
113
+ input_result = await TruGuard.input_guard.scan_async(content={"prompt": "Hello!"})
114
+ print(f"Input passed: {input_result.passed}")
115
+
116
+ output_result = await TruGuard.output_guard.scan_async(
117
+ content={"prompt": "Hi", "response": "Hi there!"}
118
+ )
119
+ print(f"Output passed: {output_result.passed}")
120
+
121
+ asyncio.run(main())
122
+ ```
123
+
124
+ ### Init options
125
+
126
+ You can pass credentials and options explicitly instead of (or in addition to) environment variables:
127
+
128
+ ```python
129
+ TruGuard.init(
130
+ application_id="your-application-id",
131
+ api_key="your-api-key",
132
+ application_version="1.0.0", # optional
133
+ environment="production", # optional: dev, staging, prod
134
+ metadata={"service": "chat"}, # optional: attached to all result uploads
135
+ batch_interval=60.0, # seconds between result uploads (default 60)
136
+ batch_count=100, # max results per batch (default 100)
137
+ debug_print=False, # set True only for debugging (adds latency)
138
+ )
139
+ ```
140
+
141
+ Guardrail configuration is fetched from the Trusys API at init and refreshed periodically in the background. No manual scanner configuration is required in your code.
142
+
143
+ ### Metadata (init + per-scan)
144
+
145
+ You can attach metadata at init (app-wide) and per scan (e.g. `conversation_id`, `session_id`). Scan-level metadata is merged with init metadata; duplicate keys are overridden by the scan value. The merged metadata is included in result uploads to the backend.
146
+
147
+ ```python
148
+ # Init with app-level metadata
149
+ TruGuard.init(
150
+ application_id="...",
151
+ api_key="...",
152
+ metadata={"environment": "prod", "service": "chat"},
153
+ )
154
+
155
+ # Per-scan metadata: merged with init metadata for this scan
156
+ input_result = TruGuard.input_guard.scan(
157
+ content={"prompt": "Hello"},
158
+ metadata={"conversation_id": "conv-123", "session_id": "sess-456"},
159
+ )
160
+ # Uploaded result includes: environment, service, conversation_id, session_id
161
+
162
+ # Same for async
163
+ output_result = await TruGuard.output_guard.scan_async(
164
+ content={"prompt": "Hi", "response": "Hi there!"},
165
+ metadata={"conversation_id": "conv-123"},
166
+ )
167
+ ```
168
+
169
+ ## Action types
170
+
171
+ When a scanner finds a violation, the action is determined by your portal configuration. Supported actions:
172
+
173
+ | Action | Behavior |
174
+ |----------|----------|
175
+ | `fix` | Apply the scanner’s suggested fix (e.g. mask PII with entity labels). |
176
+ | `mask` | Replace detected content with `****`. |
177
+ | `encrypt`| Replace with format-preserving encryption (FPE). |
178
+ | `filter` | Remove or replace problematic content. |
179
+ | `fail` | Raise `ScanFailedError` (scan fails). |
180
+ | `ignore` | Log and allow content through unchanged. |
181
+
182
+ **Format-preserving encryption (FPE)** uses the FF1/FFX algorithm. Set the `TRUSYS_FPE_KEY` environment variable if you need a custom encryption key. Decrypt with:
183
+
184
+ ```python
185
+ from trusys.actions.builtin import decrypt_text
186
+
187
+ original = decrypt_text(encrypted_text, entity_type="PHONE_NUMBER")
188
+ ```
189
+
190
+ ## Result inspection
191
+
192
+ ```python
193
+ result = TruGuard.input_guard.scan(content={"prompt": "test"})
194
+
195
+ print(result.passed)
196
+ print(result.total_execution_time_ms)
197
+
198
+ for scan_result in result.results:
199
+ print(f"{scan_result.scanner_name}: {'PASS' if scan_result.passed else 'FAIL'}")
200
+ for v in scan_result.violations:
201
+ print(f" - [{v.severity.value}] {v.message}")
202
+
203
+ all_violations = result.all_violations
204
+ failed_scanners = result.failed_scanners
205
+
206
+ if result.final_content:
207
+ print("Modified content:", result.final_content)
208
+ ```
209
+
210
+ ## Error handling
211
+
212
+ ### When the action is `fail`
213
+
214
+ If a scanner is configured with **`on_fail="fail"`** in the portal and that scanner finds a violation, the library raises **`ScanFailedError`** instead of returning an `AggregatedResult`. Your code can catch this exception to handle the failure (e.g. block the request, return an error to the user, or log and retry).
215
+
216
+ **Exception attributes:**
217
+
218
+ | Attribute | Description |
219
+ |-----------|-------------|
220
+ | `scanner_name` | Name of the scanner that failed. |
221
+ | `violations` | List of `Violation` objects (rule, severity, message, metadata such as `matched_text`). |
222
+ | `message` | Human-readable summary (e.g. `"Scan failed with N violation(s)"`). |
223
+ | `aggregated_result` | Full `AggregatedResult` for the run (all scanner results, timing). Set by the library when re-raising; useful if you need full details when handling the error. |
224
+
225
+ **Result is always saved.** Before re-raising, the library queues the failed result and flushes it to the Trusys backend. So the failure is recorded even if your application does not catch the exception and exits — you do not need to catch the exception solely to “save” the result.
226
+
227
+ **Sync example:**
228
+
229
+ ```python
230
+ from trusys.exceptions import ScanFailedError
231
+
232
+ try:
233
+ result = TruGuard.input_guard.scan(content={"prompt": "user input"})
234
+ # Use result as needed (result.passed, result.final_content, etc.)
235
+ except ScanFailedError as e:
236
+ print(f"Guardrail failed: {e.scanner_name}")
237
+ for v in e.violations:
238
+ print(f" - {v.message}")
239
+ # Optional: use full result (e.g. for logging or custom reporting)
240
+ if e.aggregated_result:
241
+ print(f"Failed scanners: {e.aggregated_result.failed_scanners}")
242
+ raise # or return an error response, etc.
243
+ ```
244
+
245
+ **Async example:**
246
+
247
+ ```python
248
+ from trusys.exceptions import ScanFailedError
249
+
250
+ try:
251
+ result = await TruGuard.input_guard.scan_async(content={"prompt": "user input"})
252
+ except ScanFailedError as e:
253
+ print(f"Guardrail failed: {e.scanner_name}, violations: {len(e.violations)}")
254
+ raise
255
+ ```
256
+
257
+ ### Other errors
258
+
259
+ ```python
260
+ from trusys.exceptions import ScanFailedError, ScannerTimeoutError
261
+
262
+ try:
263
+ result = TruGuard.input_guard.scan(content={"prompt": "content"})
264
+ except ScanFailedError as e:
265
+ print(f"Scan failed: {e.scanner_name}")
266
+ print(f"Violations: {e.violations}")
267
+ except ScannerTimeoutError as e:
268
+ print(f"Scanner {e.scanner_name} timed out after {e.timeout}s")
269
+ ```
270
+
271
+ ## Environment variables
272
+
273
+ | Variable | Description |
274
+ |----------|-------------|
275
+ | `TRUSYS_APPLICATION_ID` | Application ID from [console.trusys.ai](https://console.trusys.ai) (required for init). |
276
+ | `TRUSYS_API_KEY` | API key from the Trusys portal (required for init). |
277
+ | `TRUSYS_APPLICATION_VERSION` | Optional application version. |
278
+ | `TRUSYS_ENVIRONMENT` | Optional environment (e.g. `dev`, `staging`, `prod`). |
279
+ | `TRUSYS_BATCH_INTERVAL` | Seconds between batch uploads (default `60`). |
280
+ | `TRUSYS_BATCH_COUNT` | Max results per batch (default `100`). |
281
+ | `TRUSYS_DEBUG_PRINT` | Set to `true` for debug logging (adds latency). |
282
+ | `TRUSYS_FPE_KEY` | Optional key for format-preserving encryption. |
283
+ | `TRUSYS_API_HOST` | Backend API host (default `https://backend.trusys.ai`). |
284
+ | `TRUSYS_GUARDRAILS_HOST` | Guardrails API host (default `https://guard-api.trusys.ai`). |
285
+
286
+ ## Examples
287
+
288
+ - **Guardrail flow**: `examples/guardrail_flow_example.py` – init, then one input scan and one output scan.
289
+
290
+ Run it (with `TRUSYS_APPLICATION_ID` and `TRUSYS_API_KEY` set):
291
+
292
+ ```bash
293
+ python examples/guardrail_flow_example.py
294
+ ```
295
+
296
+ ## Development
297
+
298
+ ### Install dev dependencies
299
+
300
+ ```bash
301
+ pip install -e ".[dev]"
302
+ ```
303
+
304
+ ### Run tests
305
+
306
+ ```bash
307
+ pytest
308
+ ```
309
+
310
+ ### Run tests with coverage
311
+
312
+ ```bash
313
+ pytest --cov=trusys --cov-report=html
314
+ ```
315
+
316
+ ### Type checking
317
+
318
+ ```bash
319
+ mypy trusys
320
+ ```
321
+
322
+ ### Linting
323
+
324
+ ```bash
325
+ ruff check trusys
326
+ ```
327
+
328
+ ## License
329
+
330
+ MIT License
trusys-0.1.0/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # TruGuard
2
+
3
+ A Python library for AI guardrails. Configure your guardrails in the **Trusys portal**; in your application you run **`truguard init`** (or set credentials), call **`TruGuard.init()`**, then use the built-in **input** and **output** scanners. No manual scanner configuration in code — everything is managed from [console.trusys.ai](https://console.trusys.ai).
4
+
5
+ ## Features
6
+
7
+ - **Portal-driven configuration**: Define input and output guardrails in the [Trusys Console](https://console.trusys.ai); your app receives config automatically.
8
+ - **Static input and output guards**: Use `TruGuard.input_guard` and `TruGuard.output_guard` to scan prompts and model responses.
9
+ - **No code-level scanner setup**: Scanners (regex, blocklist, PII, content safety, etc.) are configured in the portal, not in your codebase.
10
+ - **Parallel execution**: Scanners run concurrently for better performance.
11
+ - **Flexible actions**: Fix, mask, filter, fail, or ignore violations based on portal settings.
12
+ - **Async support**: Full async/await for I/O-bound scanning.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install trusys
18
+ ```
19
+
20
+ ### Optional Dependencies
21
+
22
+ For PII (Personally Identifiable Information) detection when enabled in the portal:
23
+
24
+ ```bash
25
+ pip install trusys[pii]
26
+ python -m spacy download en_core_web_lg
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### 1. Configure guardrails in the Trusys portal
32
+
33
+ 1. Go to **[https://console.trusys.ai](https://console.trusys.ai)** and sign in.
34
+ 2. Create or select an application.
35
+ 3. Configure **input** and **output** guardrails (e.g. blocklists, PII, content safety, prompt injection).
36
+ 4. Copy your **Application ID** and **API key** for the next step.
37
+
38
+ ### 2. Initialize TruGuard in your project
39
+
40
+ Configure your Application ID and API key by setting these environment variables:
41
+
42
+ - `TRUSYS_APPLICATION_ID` – from the Trusys portal
43
+ - `TRUSYS_API_KEY` – from the Trusys portal
44
+
45
+ ### 3. Use the input and output guards in code
46
+
47
+ Call `TruGuard.init()` once at application startup (e.g. in `main` or when your app loads). Then use the static **input** and **output** guards to scan content.
48
+
49
+ ```python
50
+ from trusys import TruGuard
51
+
52
+ # One-time init at startup (uses TRUSYS_APPLICATION_ID and TRUSYS_API_KEY)
53
+ TruGuard.init()
54
+
55
+ # Scan user input (e.g. before sending to the LLM)
56
+ input_result = TruGuard.input_guard.scan(content={"prompt": "Hello world!"})
57
+ if not input_result.passed:
58
+ # Handle violations (e.g. block request or apply fixes)
59
+ for v in input_result.all_violations:
60
+ print(f"Input violation: {v.message}")
61
+ raise ValueError("Input failed guardrails")
62
+
63
+ # ... call your LLM ...
64
+
65
+ # Scan model output (e.g. before returning to the user)
66
+ output_result = TruGuard.output_guard.scan(
67
+ content={"prompt": "user prompt", "response": "model response"}
68
+ )
69
+ if not output_result.passed:
70
+ for v in output_result.all_violations:
71
+ print(f"Output violation: {v.message}")
72
+ raise ValueError("Output failed guardrails")
73
+
74
+ # Use output_result.final_content for the (possibly fixed) response if needed
75
+ ```
76
+
77
+ ### Async usage
78
+
79
+ ```python
80
+ import asyncio
81
+ from trusys import TruGuard
82
+
83
+ TruGuard.init()
84
+
85
+ async def main():
86
+ input_result = await TruGuard.input_guard.scan_async(content={"prompt": "Hello!"})
87
+ print(f"Input passed: {input_result.passed}")
88
+
89
+ output_result = await TruGuard.output_guard.scan_async(
90
+ content={"prompt": "Hi", "response": "Hi there!"}
91
+ )
92
+ print(f"Output passed: {output_result.passed}")
93
+
94
+ asyncio.run(main())
95
+ ```
96
+
97
+ ### Init options
98
+
99
+ You can pass credentials and options explicitly instead of (or in addition to) environment variables:
100
+
101
+ ```python
102
+ TruGuard.init(
103
+ application_id="your-application-id",
104
+ api_key="your-api-key",
105
+ application_version="1.0.0", # optional
106
+ environment="production", # optional: dev, staging, prod
107
+ metadata={"service": "chat"}, # optional: attached to all result uploads
108
+ batch_interval=60.0, # seconds between result uploads (default 60)
109
+ batch_count=100, # max results per batch (default 100)
110
+ debug_print=False, # set True only for debugging (adds latency)
111
+ )
112
+ ```
113
+
114
+ Guardrail configuration is fetched from the Trusys API at init and refreshed periodically in the background. No manual scanner configuration is required in your code.
115
+
116
+ ### Metadata (init + per-scan)
117
+
118
+ You can attach metadata at init (app-wide) and per scan (e.g. `conversation_id`, `session_id`). Scan-level metadata is merged with init metadata; duplicate keys are overridden by the scan value. The merged metadata is included in result uploads to the backend.
119
+
120
+ ```python
121
+ # Init with app-level metadata
122
+ TruGuard.init(
123
+ application_id="...",
124
+ api_key="...",
125
+ metadata={"environment": "prod", "service": "chat"},
126
+ )
127
+
128
+ # Per-scan metadata: merged with init metadata for this scan
129
+ input_result = TruGuard.input_guard.scan(
130
+ content={"prompt": "Hello"},
131
+ metadata={"conversation_id": "conv-123", "session_id": "sess-456"},
132
+ )
133
+ # Uploaded result includes: environment, service, conversation_id, session_id
134
+
135
+ # Same for async
136
+ output_result = await TruGuard.output_guard.scan_async(
137
+ content={"prompt": "Hi", "response": "Hi there!"},
138
+ metadata={"conversation_id": "conv-123"},
139
+ )
140
+ ```
141
+
142
+ ## Action types
143
+
144
+ When a scanner finds a violation, the action is determined by your portal configuration. Supported actions:
145
+
146
+ | Action | Behavior |
147
+ |----------|----------|
148
+ | `fix` | Apply the scanner’s suggested fix (e.g. mask PII with entity labels). |
149
+ | `mask` | Replace detected content with `****`. |
150
+ | `encrypt`| Replace with format-preserving encryption (FPE). |
151
+ | `filter` | Remove or replace problematic content. |
152
+ | `fail` | Raise `ScanFailedError` (scan fails). |
153
+ | `ignore` | Log and allow content through unchanged. |
154
+
155
+ **Format-preserving encryption (FPE)** uses the FF1/FFX algorithm. Set the `TRUSYS_FPE_KEY` environment variable if you need a custom encryption key. Decrypt with:
156
+
157
+ ```python
158
+ from trusys.actions.builtin import decrypt_text
159
+
160
+ original = decrypt_text(encrypted_text, entity_type="PHONE_NUMBER")
161
+ ```
162
+
163
+ ## Result inspection
164
+
165
+ ```python
166
+ result = TruGuard.input_guard.scan(content={"prompt": "test"})
167
+
168
+ print(result.passed)
169
+ print(result.total_execution_time_ms)
170
+
171
+ for scan_result in result.results:
172
+ print(f"{scan_result.scanner_name}: {'PASS' if scan_result.passed else 'FAIL'}")
173
+ for v in scan_result.violations:
174
+ print(f" - [{v.severity.value}] {v.message}")
175
+
176
+ all_violations = result.all_violations
177
+ failed_scanners = result.failed_scanners
178
+
179
+ if result.final_content:
180
+ print("Modified content:", result.final_content)
181
+ ```
182
+
183
+ ## Error handling
184
+
185
+ ### When the action is `fail`
186
+
187
+ If a scanner is configured with **`on_fail="fail"`** in the portal and that scanner finds a violation, the library raises **`ScanFailedError`** instead of returning an `AggregatedResult`. Your code can catch this exception to handle the failure (e.g. block the request, return an error to the user, or log and retry).
188
+
189
+ **Exception attributes:**
190
+
191
+ | Attribute | Description |
192
+ |-----------|-------------|
193
+ | `scanner_name` | Name of the scanner that failed. |
194
+ | `violations` | List of `Violation` objects (rule, severity, message, metadata such as `matched_text`). |
195
+ | `message` | Human-readable summary (e.g. `"Scan failed with N violation(s)"`). |
196
+ | `aggregated_result` | Full `AggregatedResult` for the run (all scanner results, timing). Set by the library when re-raising; useful if you need full details when handling the error. |
197
+
198
+ **Result is always saved.** Before re-raising, the library queues the failed result and flushes it to the Trusys backend. So the failure is recorded even if your application does not catch the exception and exits — you do not need to catch the exception solely to “save” the result.
199
+
200
+ **Sync example:**
201
+
202
+ ```python
203
+ from trusys.exceptions import ScanFailedError
204
+
205
+ try:
206
+ result = TruGuard.input_guard.scan(content={"prompt": "user input"})
207
+ # Use result as needed (result.passed, result.final_content, etc.)
208
+ except ScanFailedError as e:
209
+ print(f"Guardrail failed: {e.scanner_name}")
210
+ for v in e.violations:
211
+ print(f" - {v.message}")
212
+ # Optional: use full result (e.g. for logging or custom reporting)
213
+ if e.aggregated_result:
214
+ print(f"Failed scanners: {e.aggregated_result.failed_scanners}")
215
+ raise # or return an error response, etc.
216
+ ```
217
+
218
+ **Async example:**
219
+
220
+ ```python
221
+ from trusys.exceptions import ScanFailedError
222
+
223
+ try:
224
+ result = await TruGuard.input_guard.scan_async(content={"prompt": "user input"})
225
+ except ScanFailedError as e:
226
+ print(f"Guardrail failed: {e.scanner_name}, violations: {len(e.violations)}")
227
+ raise
228
+ ```
229
+
230
+ ### Other errors
231
+
232
+ ```python
233
+ from trusys.exceptions import ScanFailedError, ScannerTimeoutError
234
+
235
+ try:
236
+ result = TruGuard.input_guard.scan(content={"prompt": "content"})
237
+ except ScanFailedError as e:
238
+ print(f"Scan failed: {e.scanner_name}")
239
+ print(f"Violations: {e.violations}")
240
+ except ScannerTimeoutError as e:
241
+ print(f"Scanner {e.scanner_name} timed out after {e.timeout}s")
242
+ ```
243
+
244
+ ## Environment variables
245
+
246
+ | Variable | Description |
247
+ |----------|-------------|
248
+ | `TRUSYS_APPLICATION_ID` | Application ID from [console.trusys.ai](https://console.trusys.ai) (required for init). |
249
+ | `TRUSYS_API_KEY` | API key from the Trusys portal (required for init). |
250
+ | `TRUSYS_APPLICATION_VERSION` | Optional application version. |
251
+ | `TRUSYS_ENVIRONMENT` | Optional environment (e.g. `dev`, `staging`, `prod`). |
252
+ | `TRUSYS_BATCH_INTERVAL` | Seconds between batch uploads (default `60`). |
253
+ | `TRUSYS_BATCH_COUNT` | Max results per batch (default `100`). |
254
+ | `TRUSYS_DEBUG_PRINT` | Set to `true` for debug logging (adds latency). |
255
+ | `TRUSYS_FPE_KEY` | Optional key for format-preserving encryption. |
256
+ | `TRUSYS_API_HOST` | Backend API host (default `https://backend.trusys.ai`). |
257
+ | `TRUSYS_GUARDRAILS_HOST` | Guardrails API host (default `https://guard-api.trusys.ai`). |
258
+
259
+ ## Examples
260
+
261
+ - **Guardrail flow**: `examples/guardrail_flow_example.py` – init, then one input scan and one output scan.
262
+
263
+ Run it (with `TRUSYS_APPLICATION_ID` and `TRUSYS_API_KEY` set):
264
+
265
+ ```bash
266
+ python examples/guardrail_flow_example.py
267
+ ```
268
+
269
+ ## Development
270
+
271
+ ### Install dev dependencies
272
+
273
+ ```bash
274
+ pip install -e ".[dev]"
275
+ ```
276
+
277
+ ### Run tests
278
+
279
+ ```bash
280
+ pytest
281
+ ```
282
+
283
+ ### Run tests with coverage
284
+
285
+ ```bash
286
+ pytest --cov=trusys --cov-report=html
287
+ ```
288
+
289
+ ### Type checking
290
+
291
+ ```bash
292
+ mypy trusys
293
+ ```
294
+
295
+ ### Linting
296
+
297
+ ```bash
298
+ ruff check trusys
299
+ ```
300
+
301
+ ## License
302
+
303
+ MIT License
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "trusys"
7
+ version = "0.1.0"
8
+ description = "A modular Python library for AI guardrails with input/output scanning capabilities"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ classifiers = [
13
+ "Intended Audience :: Developers",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
19
+ ]
20
+ keywords = ["ai", "guardrails", "llm", "safety", "scanning"]
21
+
22
+ dependencies = [
23
+ "fuzzysearch>=0.7.0", # For fuzzy matching in BlockListScanner
24
+ "httpx>=0.27.0", # HTTP client for guardrail config fetch, save-results, and remote scanner calls
25
+ "pyffx>=0.3.0", # For FF1/FFX format-preserving encryption
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ dev = [
30
+ "pytest>=7.0",
31
+ "pytest-asyncio>=0.21",
32
+ "pytest-cov>=4.0",
33
+ "mypy>=1.0",
34
+ "ruff>=0.1",
35
+ ]
36
+ pii = [
37
+ "presidio-analyzer>=2.2.0",
38
+ "spacy>=3.0.0",
39
+ ]
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["."]
43
+ include = ["trusys*"]
44
+
45
+ [tool.pytest.ini_options]
46
+ asyncio_mode = "auto"
47
+ testpaths = ["tests"]
48
+
49
+ [tool.mypy]
50
+ python_version = "3.10"
51
+ strict = true
52
+ warn_return_any = true
53
+ warn_unused_ignores = true
54
+
55
+ [tool.ruff]
56
+ line-length = 100
57
+ target-version = "py310"
trusys-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+