voicesafekit 0.6.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 (39) hide show
  1. voicesafekit-0.6.0/LICENSE +22 -0
  2. voicesafekit-0.6.0/PKG-INFO +425 -0
  3. voicesafekit-0.6.0/README.md +377 -0
  4. voicesafekit-0.6.0/pyproject.toml +81 -0
  5. voicesafekit-0.6.0/setup.cfg +4 -0
  6. voicesafekit-0.6.0/tests/test_audio.py +357 -0
  7. voicesafekit-0.6.0/tests/test_cli_encryption.py +53 -0
  8. voicesafekit-0.6.0/tests/test_cli_redact.py +22 -0
  9. voicesafekit-0.6.0/tests/test_encryption.py +23 -0
  10. voicesafekit-0.6.0/tests/test_engine.py +228 -0
  11. voicesafekit-0.6.0/tests/test_litellm.py +229 -0
  12. voicesafekit-0.6.0/tests/test_livekit.py +58 -0
  13. voicesafekit-0.6.0/tests/test_openvoiceos.py +24 -0
  14. voicesafekit-0.6.0/tests/test_openwebui.py +298 -0
  15. voicesafekit-0.6.0/tests/test_pipecat.py +151 -0
  16. voicesafekit-0.6.0/tests/test_session.py +59 -0
  17. voicesafekit-0.6.0/tests/test_spoken.py +215 -0
  18. voicesafekit-0.6.0/voicesafekit/__init__.py +21 -0
  19. voicesafekit-0.6.0/voicesafekit/__main__.py +5 -0
  20. voicesafekit-0.6.0/voicesafekit/audio.py +506 -0
  21. voicesafekit-0.6.0/voicesafekit/cli.py +297 -0
  22. voicesafekit-0.6.0/voicesafekit/encryption.py +94 -0
  23. voicesafekit-0.6.0/voicesafekit/engine.py +413 -0
  24. voicesafekit-0.6.0/voicesafekit/integrations/__init__.py +2 -0
  25. voicesafekit-0.6.0/voicesafekit/integrations/litellm.py +249 -0
  26. voicesafekit-0.6.0/voicesafekit/integrations/livekit.py +103 -0
  27. voicesafekit-0.6.0/voicesafekit/integrations/openvoiceos.py +61 -0
  28. voicesafekit-0.6.0/voicesafekit/integrations/openwebui.py +407 -0
  29. voicesafekit-0.6.0/voicesafekit/integrations/pipecat.py +234 -0
  30. voicesafekit-0.6.0/voicesafekit/models.py +77 -0
  31. voicesafekit-0.6.0/voicesafekit/rules.py +424 -0
  32. voicesafekit-0.6.0/voicesafekit/session.py +194 -0
  33. voicesafekit-0.6.0/voicesafekit/spoken.py +395 -0
  34. voicesafekit-0.6.0/voicesafekit.egg-info/PKG-INFO +425 -0
  35. voicesafekit-0.6.0/voicesafekit.egg-info/SOURCES.txt +37 -0
  36. voicesafekit-0.6.0/voicesafekit.egg-info/dependency_links.txt +1 -0
  37. voicesafekit-0.6.0/voicesafekit.egg-info/entry_points.txt +2 -0
  38. voicesafekit-0.6.0/voicesafekit.egg-info/requires.txt +25 -0
  39. voicesafekit-0.6.0/voicesafekit.egg-info/top_level.txt +1 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mohamad Kanso
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,425 @@
1
+ Metadata-Version: 2.4
2
+ Name: voicesafekit
3
+ Version: 0.6.0
4
+ Summary: Catch spoken PII (card numbers, SSNs, emails said out loud) before voice transcripts reach an LLM. Zero dependencies, Pipecat and LiveKit drop-ins.
5
+ Author: Mohamad Kanso
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/MohamadKanso/VoiceSafeKit
8
+ Project-URL: Repository, https://github.com/MohamadKanso/VoiceSafeKit
9
+ Project-URL: Demo, https://mohamadkanso.github.io/VoiceSafeKit/
10
+ Project-URL: Changelog, https://github.com/MohamadKanso/VoiceSafeKit/blob/main/CHANGELOG.md
11
+ Project-URL: Issues, https://github.com/MohamadKanso/VoiceSafeKit/issues
12
+ Project-URL: Benchmark, https://github.com/MohamadKanso/VoiceSafeKit/tree/main/benchmarks
13
+ Keywords: voice-assistant,llm-safety,privacy,local-ai,openvoiceos,wyoming,pii-detection,conversation-safety,pii-redaction,speech-to-text,pipecat,livekit,voice-agents,guardrails,open-webui,litellm,audio-redaction
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Security
22
+ Classifier: Operating System :: OS Independent
23
+ Classifier: Intended Audience :: Information Technology
24
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
25
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Provides-Extra: crypto
30
+ Requires-Dist: cryptography>=42; extra == "crypto"
31
+ Provides-Extra: pipecat
32
+ Requires-Dist: pipecat-ai>=1.0; extra == "pipecat"
33
+ Provides-Extra: livekit
34
+ Requires-Dist: livekit-agents>=1.0; extra == "livekit"
35
+ Provides-Extra: audio
36
+ Requires-Dist: faster-whisper>=1.0; extra == "audio"
37
+ Requires-Dist: numpy; extra == "audio"
38
+ Provides-Extra: openwebui
39
+ Requires-Dist: pydantic>=2; extra == "openwebui"
40
+ Provides-Extra: litellm
41
+ Requires-Dist: litellm[proxy]>=1.80; extra == "litellm"
42
+ Provides-Extra: dev
43
+ Requires-Dist: build>=1.2; extra == "dev"
44
+ Requires-Dist: pytest>=8.0; extra == "dev"
45
+ Requires-Dist: ruff>=0.6; extra == "dev"
46
+ Requires-Dist: cryptography>=42; extra == "dev"
47
+ Dynamic: license-file
48
+
49
+ <p align="center">
50
+ <img src="https://raw.githubusercontent.com/MohamadKanso/VoiceSafeKit/main/docs/assets/voicesafekit-cover.svg" alt="VoiceSafeKit" width="760">
51
+ </p>
52
+
53
+ <p align="center">
54
+ <a href="https://github.com/MohamadKanso/VoiceSafeKit/actions/workflows/ci.yml"><img src="https://github.com/MohamadKanso/VoiceSafeKit/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
55
+ <img src="https://img.shields.io/badge/dependencies-0-00ff84" alt="Zero dependencies">
56
+ <img src="https://img.shields.io/badge/python-3.10%E2%80%933.13-00ff84" alt="Python 3.10-3.13">
57
+ <img src="https://img.shields.io/badge/license-MIT-00ff84" alt="MIT">
58
+ </p>
59
+
60
+ <p align="center">
61
+ <a href="https://mohamadkanso.github.io/VoiceSafeKit/"><strong>Try it in your browser</strong></a>
62
+ ·
63
+ <a href="#spokenpii-benchmark"><strong>Benchmark</strong></a>
64
+ ·
65
+ <a href="#pipecat">Pipecat</a>
66
+ ·
67
+ <a href="#livekit-agents">LiveKit</a>
68
+ ·
69
+ <a href="#open-webui">Open WebUI</a>
70
+ ·
71
+ <a href="#litellm">LiteLLM</a>
72
+ ·
73
+ <a href="#bleep-the-recording-not-just-the-transcript">Audio</a>
74
+ ·
75
+ <a href="https://github.com/MohamadKanso/VoiceSafeKit/blob/main/CHANGELOG.md">Changelog</a>
76
+ </p>
77
+
78
+ # VoiceSafeKit
79
+
80
+ **Your voice agent is sending card numbers to the LLM, and your PII filter can't see them.**
81
+
82
+ <p align="center">
83
+ <img src="https://raw.githubusercontent.com/MohamadKanso/VoiceSafeKit/main/docs/assets/demo.gif" alt="A spoken card number and email are recorded in the browser, transcribed by Whisper on-device, and both are flagged for redaction" width="860">
84
+ <br>
85
+ <sub>A voice says a card number and an email into the <a href="https://mohamadkanso.github.io/VoiceSafeKit/">live app</a>. Whisper runs on-device and both are caught. Recording sped up 5×.</sub>
86
+ </p>
87
+
88
+ Speech-to-text writes what people *say*, not what they *mean*:
89
+
90
+ ```text
91
+ my card is four two four two four two four two four two four two four two four two
92
+ email me at alex dot rivera at gmail dot com
93
+ call me on oh seven seven double oh nine double oh one two three
94
+ ```
95
+
96
+ Regex filters and NER models (Presidio, most guardrail libraries) are built for written
97
+ text, so they pass all three straight through. VoiceSafeKit reads speech the way STT
98
+ writes it, redacts the sensitive parts, and gives the values back when the agent needs
99
+ to say them. It can also bleep them out of the call recording. Zero dependencies, about
100
+ 0.06 ms per utterance, and drop-ins for **Pipecat**, **LiveKit Agents**, **Open WebUI** and
101
+ **LiteLLM**.
102
+
103
+ ```text
104
+ User says : Hi, my name is Alex Rivera. My card number is four two four two four two four two
105
+ four two four two four two four two and my email is alex dot rivera at gmail dot com.
106
+ LLM receives : Hi, my name is [NAME_1]. My card number is [CARD_1] and my email is [EMAIL_1].
107
+ TTS says : Thanks Alex Rivera. I'll send the receipt to alex dot rivera at gmail dot com.
108
+ ```
109
+
110
+ <sub>Output of `examples/pipecat/offline_demo.py`, which runs without API keys.</sub>
111
+
112
+ ## Install
113
+
114
+ ```bash
115
+ pip install voicesafekit
116
+ # or, before the first PyPI release: pip install git+https://github.com/MohamadKanso/VoiceSafeKit
117
+ # extras: [pipecat] [livekit] [openwebui] [litellm] [audio] [crypto]
118
+ ```
119
+
120
+ ```bash
121
+ echo "my pin is four eight two one" | voicesafekit redact
122
+ # my pin is [PIN removed]
123
+ ```
124
+
125
+ ## See it
126
+
127
+ Spoken PII caught in the [live app](https://mohamadkanso.github.io/VoiceSafeKit/), with no
128
+ digits or @ signs anywhere in the transcript:
129
+
130
+ <p align="center">
131
+ <img src="https://raw.githubusercontent.com/MohamadKanso/VoiceSafeKit/main/docs/assets/screenshot-1-transcript.png" alt="Capture and Check panels: a spoken card number, email and one-time code are each flagged and marked caught in spoken form" width="900">
132
+ </p>
133
+
134
+ <table>
135
+ <tr>
136
+ <td width="50%"><img src="https://raw.githubusercontent.com/MohamadKanso/VoiceSafeKit/main/docs/assets/screenshot-2-risks.png" alt="Inspect tab highlighting the spoken name, card number, email and code in the original transcript"></td>
137
+ <td width="50%"><img src="https://raw.githubusercontent.com/MohamadKanso/VoiceSafeKit/main/docs/assets/screenshot-3-protect.png" alt="Protect panel with the safe transcript and guidance for the assistant"></td>
138
+ </tr>
139
+ <tr>
140
+ <td><sub>Inspect: the exact spoken words that were flagged.</sub></td>
141
+ <td><sub>Protect: the safe transcript and guidance for the assistant.</sub></td>
142
+ </tr>
143
+ </table>
144
+
145
+ <p align="center">
146
+ <img src="https://raw.githubusercontent.com/MohamadKanso/VoiceSafeKit/main/docs/assets/screenshot-4-spoken-vs-text.png" alt="Table comparing a text-only filter, which marks spoken PII as SAFE, with VoiceSafeKit, which redacts it" width="900">
147
+ </p>
148
+
149
+ ## SpokenPII benchmark
150
+
151
+ No public benchmark measures PII *as speech-to-text writes it*, so this repo ships one.
152
+
153
+ **Held-out real speech (the honest number).** New scripts read aloud by a British TTS
154
+ voice (`Daniel`), transcribed by faster-whisper `small.en`, frozen before any 0.6 change
155
+ and never used for tuning. A sample counts as protected only if the value can't be
156
+ recovered from the redacted transcript.
157
+
158
+ | Detector | PII protected (not leaked) | False alarms on no-PII |
159
+ |---|---:|---:|
160
+ | **VoiceSafeKit 0.6** | **98%** (106/108) | **2%** (1/40) |
161
+ | VoiceSafeKit 0.5 | 94% (102/108) | 2% (1/40) |
162
+ | VoiceSafeKit 0.6, spoken mode off | 87% (94/108) | 2% (1/40) |
163
+ | Microsoft Presidio 2.2 | 65% (70/108) | 42% (17/40) |
164
+
165
+ The three failures are published in
166
+ [#9](https://github.com/MohamadKanso/VoiceSafeKit/issues/9): a PIN Whisper wrote as money
167
+ (`$4,990`), a comma before the birth year, and a mangled date read as a phone number.
168
+
169
+ <details>
170
+ <summary>First real-speech set (Samantha voice, Whisper base.en)</summary>
171
+
172
+ v0.5 protected 101/108 on this set. Its 7 misses (comma-split digit groups, `SSN609507896`,
173
+ digits written as decimals, "fin" for PIN) were fixed in 0.6
174
+ ([#6](https://github.com/MohamadKanso/VoiceSafeKit/issues/6)), so 0.6 now scores 108/108
175
+ here. Because the fixes were written against these misses, this set is no longer held out.
176
+ Presidio: 79/108, 17/40 false alarms.
177
+
178
+ </details>
179
+
180
+ **SpokenPII (synthetic).** 540 labelled sentences, the same values written two ways, plus
181
+ 40 tricky no-PII requests ("emergency fund", "call Sue", "twenty five minutes"). A span
182
+ counts as protected only if at least 90% of it is masked.
183
+
184
+ | Detector | Written PII protected | Spoken PII protected | False alarms on no-PII | Median latency |
185
+ |---|---:|---:|---:|---:|
186
+ | **VoiceSafeKit 0.6** | **100%** | **100%** | **0%** | **0.06 ms** |
187
+ | VoiceSafeKit 0.6, spoken mode off | 100% | 11% | 0% | 0.04 ms |
188
+ | Microsoft Presidio 2.2 (spaCy `en_core_web_lg`) | 86% | 21% | 45% | ~3 ms |
189
+
190
+ Spoken PII protected, by type:
191
+
192
+ | Type | VoiceSafeKit | Presidio |
193
+ |---|---:|---:|
194
+ | Card number | 100% | 0% |
195
+ | SSN | 100% | 3% |
196
+ | Phone | 100% | 0% |
197
+ | Email | 100% | 0% |
198
+ | PIN | 100% | 13% |
199
+ | One-time code | 100% | 3% |
200
+ | Passport | 100% | 0% |
201
+ | Date of birth | 100% | 77% |
202
+ | Name | 100% | 97% |
203
+
204
+ **Read these numbers honestly.** I wrote the synthetic set and the detector, so 100% there
205
+ mostly shows the templates are covered; the held-out real-speech set is the fairer test. Presidio is a
206
+ general-purpose tool configured with its defaults, not tuned for speech. Its false alarms
207
+ are mostly `DATE_TIME` and `PERSON` hits on ordinary requests. Reproduce everything with
208
+ `python benchmarks/run.py --presidio` and
209
+ `python benchmarks/asr.py score --presidio --data benchmarks/data/spokenpii-asr-heldout.jsonl`.
210
+ Better still, add transcripts from your own STT engine.
211
+
212
+ ## What it catches
213
+
214
+ | Category | Detectors |
215
+ |---|---|
216
+ | Spoken forms | digits as words, "oh" / "double" / "triple", "plus four four", tens and hundreds, spoken emails, spoken dates |
217
+ | Payment | card number (Luhn + context), partial card ("ends in 4821"), CVV, PIN, IBAN (mod-97) |
218
+ | Identity | SSN (with or without dashes), passport, UK National Insurance, date of birth, self-introduced name |
219
+ | Contact | email, phone, street address, UK postcode, IP address |
220
+ | Credentials | passwords and secrets in speech, one-time / verification codes, API keys (OpenAI, AWS, GitHub, Slack, Google) |
221
+ | Safety signals | emergency, emotional distress, coercion, medical / legal / financial advice requests |
222
+
223
+ Every result has a `decision` (`SAFE`, `REDACT`, `REVIEW`, `BLOCK`), a 0-100 `score`,
224
+ per-finding `confidence` and `spoken` flags, a `safe_transcript`, a `redaction_map` and
225
+ plain-English `assistant_guidance`.
226
+
227
+ ## Python API
228
+
229
+ ```python
230
+ from voicesafekit import analyze_transcript
231
+
232
+ result = analyze_transcript("my social is four seven one, five five, eight eight four three")
233
+ result.decision # "BLOCK"
234
+ result.safe_transcript # "my social is [SSN removed]"
235
+ result.findings[0].spoken # True
236
+ ```
237
+
238
+ ### Reversible placeholders
239
+
240
+ Plain redaction breaks agents that need to say "I've sent it to your email". A
241
+ `PrivacySession` swaps values for placeholders, keeps the map in memory, and restores
242
+ them in the reply, including streamed tokens:
243
+
244
+ ```python
245
+ from voicesafekit import PrivacySession
246
+
247
+ session = PrivacySession()
248
+ safe = session.protect("email me at alex dot rivera at gmail dot com")
249
+ safe.text # "email me at [EMAIL_1]"
250
+ reply = llm(safe.text) # "Done, I've emailed [EMAIL_1]."
251
+ session.restore(reply) # "Done, I've emailed alex dot rivera at gmail dot com."
252
+
253
+ stream = session.stream() # for token streams: "[EMA" + "IL_1]" restores correctly
254
+ ```
255
+
256
+ The same value gets the same placeholder for the whole conversation, whether it was
257
+ spoken or written. **Passwords, PINs, CVVs, one-time codes and API keys are never stored,**
258
+ so the model can't make the voice read them back.
259
+
260
+ ## Pipecat
261
+
262
+ ```python
263
+ from voicesafekit.integrations.pipecat import voicesafe_processors
264
+
265
+ guard_in, restore_out, guard_context = voicesafe_processors()
266
+
267
+ pipeline = Pipeline([
268
+ transport.input(), stt,
269
+ guard_in, # spoken PII -> [CARD_1], [EMAIL_1] before the LLM
270
+ user_aggregator, llm,
271
+ restore_out, # real values back for the voice only
272
+ tts, transport.output(),
273
+ guard_context, # stored assistant replies keep the placeholders
274
+ assistant_aggregator,
275
+ ])
276
+
277
+ @guard_in.event_handler("on_block")
278
+ async def on_block(processor, protected):
279
+ ... # emergencies, government IDs, CVVs: never forwarded, refusal is spoken
280
+ ```
281
+
282
+ Many STT services send several final transcriptions per turn. When one ends mid-number near
283
+ a sensitive word ("my card number is four two four two"), the processor holds it for up to
284
+ 0.8 s and merges it with the next, so a card number split across frames is still caught.
285
+
286
+ ## LiveKit Agents
287
+
288
+ ```python
289
+ from voicesafekit.integrations.livekit import VoiceSafeAgent
290
+
291
+ class Assistant(VoiceSafeAgent): # instead of Agent
292
+ def __init__(self):
293
+ super().__init__(instructions="You are a helpful voice assistant.")
294
+ ```
295
+
296
+ `VoiceSafeAgent` protects each user turn in `on_user_turn_completed`, restores values in
297
+ `tts_node`, and stops blocked turns. `VoiceSafeRedactor().redact(text)` is a stateless,
298
+ idempotent redactor for transcript and log hooks. Full example: `examples/livekit/agent.py`.
299
+
300
+ ## Open WebUI
301
+
302
+ A Filter Function: **Admin Panel > Functions > + > paste
303
+ [`examples/openwebui/voicesafekit_filter.py`](https://github.com/MohamadKanso/VoiceSafeKit/blob/main/examples/openwebui/voicesafekit_filter.py) > Save**,
304
+ then enable it. It installs VoiceSafeKit through its `requirements:` line.
305
+
306
+ - Every user message is protected before the model sees it ("my card is four two four
307
+ two ..." becomes "my card is [CARD_1]"), and real values come back in the reply on the
308
+ user's own screen, streamed or not.
309
+ - Placeholders stay consistent per chat and per user. A new message carrying a government
310
+ ID, card security code or emergency is stopped with a clear message (valve
311
+ `block_on_critical`).
312
+ - Valves: `spoken`, `block_on_critical`, `restore_in_output`, `max_chats`, `priority`.
313
+
314
+ Open WebUI's background tasks (title, tags, follow-ups, autocomplete) call the task model
315
+ directly and skip filters, so turn them off or use a local task model for full privacy.
316
+ Built against Open WebUI 0.13 and tested with the request shapes from its source.
317
+
318
+ ## LiteLLM
319
+
320
+ A custom guardrail for the LiteLLM proxy, so every app behind it gets the same protection:
321
+
322
+ ```yaml
323
+ guardrails:
324
+ - guardrail_name: voicesafekit
325
+ litellm_params:
326
+ guardrail: voicesafekit.integrations.litellm.VoiceSafeGuardrail
327
+ mode: [pre_call, post_call] # redact before the model, restore after (streaming too)
328
+ default_on: true
329
+ block_on_critical: true # HTTP 400 for government IDs, CVVs, emergencies
330
+ ```
331
+
332
+ ```bash
333
+ pip install "voicesafekit[litellm]"
334
+ litellm --config config.yaml
335
+ ```
336
+
337
+ Tested on a running LiteLLM 1.108 proxy: the upstream model received `my card is [CARD_1]`,
338
+ the client got the real values back (normal and streamed), and an SSN request returned
339
+ HTTP 400 without reaching the model. See [`examples/litellm/`](https://github.com/MohamadKanso/VoiceSafeKit/tree/main/examples/litellm/).
340
+
341
+ ## Bleep the recording, not just the transcript
342
+
343
+ Redacting the transcript still leaves the card number in the call recording. VoiceSafeKit
344
+ bleeps it out of the audio too, locally and for free, with standard-library code once you
345
+ have word timestamps.
346
+
347
+ ```bash
348
+ pip install "voicesafekit[audio]" # adds faster-whisper for word timestamps
349
+ voicesafekit bleep call.wav -o call.bleeped.wav
350
+ # Hi, my name is [name removed], my card number is [payment number removed] and my pin is [PIN removed].
351
+ #
352
+ # Bleeped 3 span(s) -> call.bleeped.wav
353
+ # 00:00.950 - 00:01.870 Person name
354
+ # 00:02.990 - 00:07.330 Payment card-like number
355
+ # 00:07.730 - 00:08.850 PIN
356
+ ```
357
+
358
+ Already have word timings from Deepgram, Whisper or WhisperX? Pass them and no extras are
359
+ needed:
360
+
361
+ ```python
362
+ from voicesafekit.audio import bleep_wav, redact_audio_spans, words_from_deepgram
363
+
364
+ redaction = redact_audio_spans(words_from_deepgram(response)) # PII -> time spans
365
+ bleep_wav("call.wav", "call.bleeped.wav", redaction) # or mode="silence"
366
+ ```
367
+
368
+ Checked end to end: transcribing the bleeped file with Whisper finds no card or PIN digits,
369
+ while the rest of the sentence still comes through. PCM WAV (8/16/24/32-bit, any channel count), streamed in
370
+ chunks; every sample outside the spans is copied byte for byte.
371
+
372
+ ## OpenVoiceOS
373
+
374
+ ```python
375
+ from voicesafekit.integrations.openvoiceos import protect_utterance
376
+
377
+ result = protect_utterance(utterance)
378
+ if result.should_continue:
379
+ send_to_llm(result.safe_transcript)
380
+ ```
381
+
382
+ ## CLI
383
+
384
+ ```bash
385
+ voicesafekit check examples/transcripts/identity_theft.txt --pretty
386
+ voicesafekit redact --placeholders < transcript.txt
387
+ voicesafekit conversation turn1.txt turn2.txt --pretty
388
+ voicesafekit bleep call.wav -o call.bleeped.wav # needs [audio] or --words
389
+ voicesafekit check transcript.txt --out result.enc.json # AES-256-GCM, needs [crypto]
390
+ ```
391
+
392
+ ## Browser app
393
+
394
+ The [live app](https://mohamadkanso.github.io/VoiceSafeKit/) records your voice,
395
+ transcribes it with Whisper on your device and runs the same rules in the browser. No
396
+ server and no API key, and nothing leaves the page. Say a card number out loud and watch
397
+ it disappear.
398
+
399
+ ```bash
400
+ python3 -m http.server 8124 --directory docs # run it locally
401
+ ```
402
+
403
+ ## Design principles
404
+
405
+ - **Speech first.** Rules run on what STT actually produces, not on tidy written text.
406
+ - **Local and tiny.** Pure Python, no dependencies, no network, no model download.
407
+ - **Auditable.** Every finding has a confidence score and maps to exact spans.
408
+ - **Fail toward privacy.** Credentials are never stored, blocked turns never reach the model.
409
+ - **Honest scope.** It is a fast first layer, not a full privacy programme or a
410
+ compliance certification. Pair it with your own review for PCI DSS, HIPAA or GDPR work.
411
+
412
+ ## Contributing
413
+
414
+ The most valuable contribution is a transcript your STT engine produced that VoiceSafeKit
415
+ missed (fake data only). Use the
416
+ [Missed spoken PII](https://github.com/MohamadKanso/VoiceSafeKit/issues/new?template=missed_spoken_pii.yml)
417
+ template. See [CONTRIBUTING.md](https://github.com/MohamadKanso/VoiceSafeKit/blob/main/CONTRIBUTING.md) and the [roadmap](https://github.com/MohamadKanso/VoiceSafeKit/blob/main/ROADMAP.md).
418
+
419
+ ```bash
420
+ pip install -e ".[dev]" && pytest && python benchmarks/run.py
421
+ ```
422
+
423
+ ## License
424
+
425
+ MIT. See [LICENSE](https://github.com/MohamadKanso/VoiceSafeKit/blob/main/LICENSE). Not legal, medical or security advice.