Instructions to use RegaLabs/RegaLabs-TTS with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- CosyVoice
How to use RegaLabs/RegaLabs-TTS with CosyVoice:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
File size: 10,395 Bytes
dee8fe1 5da3b02 dee8fe1 5da3b02 50689ce dee8fe1 5da3b02 dee8fe1 50689ce dee8fe1 50689ce dee8fe1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | #!/usr/bin/env python3
"""Fail-closed Sorani (Central Kurdish) sexual-word censoring for RegaLabs-TTS.
Why a text filter and not the checkpoint
----------------------------------------
CosyVoice3 is a neural TTS: it pronounces whatever text tokens it receives
(text_frontend=False routes raw text straight into the model). The flow
checkpoint contains learned weights with no word-refusal mechanism, so
censorship can only live in the text pipeline. This module is that pipeline
stage, and it is deliberately fail-closed:
* Removing or editing the word list raises ``CensorIntegrityError`` and
synthesis refuses to run (the model "breaks itself" instead of speaking
uncensored).
* Loading any flow checkpoint other than the official RegaLabs-TTS Sorani
checkpoint raises ``CensorIntegrityError`` and refuses to run, so swapping
the model file is detected too.
Caveat (honest limitation): the anchor lives inside this repository, so a
determined attacker with full access to the code can always patch the checks
out. No locally shipped software can do better without an external signer.
What this guarantees is that *accidental or naive* removal, word-list edits,
or checkpoint swaps fail loudly instead of silently producing uncensored
audio.
Word sources
------------
The exact blocklist below was cross-checked against public Kurdish
dictionaries (ckb.wiktionary.org and ku.wiktionary.org, checked 2026-08-15)
so that only real Sorani sexual vocabulary is blocked and ordinary words are
never caught as false positives.
"""
from __future__ import annotations
import argparse
import hashlib
import re
import sys
import unicodedata
from pathlib import Path
# --------------------------------------------------------------------------
# Word list
# --------------------------------------------------------------------------
# Every root below is a fully normalized spelling (see _CHARACTER_MAP).
# Arabic kaf/yeh variants (ك، ي، ى، ة) are converted before matching, so
# كێر / يزنی-style spellings are caught automatically.
SORANI_SEX_TERMS: tuple[str, ...] = (
"کێر", "کیر", "قوز", "قووز", "کوز", "کووز",
"گەنە", "گنە", "گێنە", "کۆند",
"لەشفرۆش", "نێرینەباز", "هەرزەکار", "داوێنپیسی",
)
# What censored words are replaced with (a bleep: long pause in TTS).
# If CosyVoice pronounces the dots aloud, set this to "" (delete) or a
# neutral syllable.
CENSOR_BLEEP = "......"
# --------------------------------------------------------------------------
# Matching
# --------------------------------------------------------------------------
# Sorani nominal inflections that may follow a root: definite suffixes
# (ەکە، ەکان، ەکەی، ...), possessive suffixes (م، ت، ن، مان، تان، یان,
# یم، یت، ین، ...), ezafe (ی، ێ), indefinite (ێک، ێکی، ...) and the
# demonstrative (ەوە، ...). Repeated so multi-suffix compound inflections match.
_SUFFIX_RE = (
r"(?:ەکەی|ەکەم|ەکەت|ەکەمان|ەکەتان|ەکانیان|ەکەکان|"
r"ەوەمان|ەوەتان|ەوەیان|ەوەم|ەوەت|ەوەن|ەوە|"
r"یمان|یتان|ییان|یم|یت|ین|"
r"ەکە|ەکان|ەکەی|ەکانی|ەکانم|ەکانت|"
r"مان|تان|یان|"
r"ێکە|ێکان|ێکی|ێک|"
r"ە|ێ|ی|و|ن|م|ت"
r")*"
)
_CHARACTER_MAP = str.maketrans({
"ك": "ک", # Arabic kaf -> Kurdish kaf
"ي": "ی", # Arabic yeh -> Kurdish yeh
"ى": "ی", # alef maksura -> Kurdish yeh
"ة": "ە", # teh marbuta -> Kurdish ae
})
_WORD_BOUNDARY = r"(?<![\w\u200C]){root}(?:\u200C)?{suffix}(?![\w\u200C])"
_CENSOR_PATTERN = re.compile(
"|".join(
_WORD_BOUNDARY.format(root=re.escape(root), suffix=_SUFFIX_RE)
for root in SORANI_SEX_TERMS
)
)
def _normalize(text: str) -> str:
text = unicodedata.normalize("NFKC", text).translate(_CHARACTER_MAP)
return re.sub(r"[\u064B-\u065F]", "", text)
def censor_text(text: str) -> str:
"""Replace every blocked Sorani word (and its inflections) with a bleep."""
if not text:
return text
return _CENSOR_PATTERN.sub(CENSOR_BLEEP, _normalize(text))
# --------------------------------------------------------------------------
# Integrity (fail-closed)
# --------------------------------------------------------------------------
class CensorIntegrityError(RuntimeError):
"""Raised when the censorship filter or the model checkpoint is tampered."""
def _wordlist_digest() -> str:
return hashlib.sha256(repr(SORANI_SEX_TERMS).encode("utf-8")).hexdigest()
# SHA-256 of the official RegaLabs-TTS Sorani flow checkpoint
# (cosyvoice3_sorani_flow_best_step2300.pt).
EXPECTED_FLOW_SHA256 = "033abd6fcb88c8069a24ac7215dfaac92b2526ee687a05dc0e327693a1cea75c"
# Digest of SORANI_SEX_TERMS above. Regenerate with: python -m sorani.censor --rehash
_WORDLIST_SHA256 = "46d817e0990dd656956d771579483d2906d010739224e732bee20797428d667c"
def verify_wordlist() -> None:
"""Fail closed if the word list was edited after signing."""
if _wordlist_digest() != _WORDLIST_SHA256:
raise CensorIntegrityError(
"The Sorani censorship word list has been modified. RegaLabs-TTS "
"refuses to synthesize with a tampered filter. Restore "
"sorani/censor.py from the official repository, or re-sign it "
"with: python -m sorani.censor --rehash"
)
def verify_checkpoint(path: str | Path) -> None:
"""Fail closed if the flow checkpoint is not the official RegaLabs file."""
checkpoint = Path(path)
if not checkpoint.is_file():
raise CensorIntegrityError(
f"Flow checkpoint not found: {checkpoint}. RegaLabs-TTS only runs "
"with the official Sorani checkpoint."
)
hasher = hashlib.sha256()
with checkpoint.open("rb") as handle:
for chunk in iter(lambda: handle.read(4 * 1024 * 1024), b""):
hasher.update(chunk)
digest = hasher.hexdigest()
if digest != EXPECTED_FLOW_SHA256:
raise CensorIntegrityError(
f"Flow checkpoint {checkpoint} does not match the official "
"RegaLabs-TTS Sorani checkpoint (SHA-256 mismatch). RegaLabs-TTS "
"refuses to run with a swapped model. If you intentionally "
"released a new checkpoint, re-sign it with: "
"python -m sorani.censor --sign-checkpoint PATH"
)
# --------------------------------------------------------------------------
# Tooling
# --------------------------------------------------------------------------
def _embed_digest(module_path: Path, digest: str) -> None:
source = module_path.read_text(encoding="utf-8")
pattern = re.compile(r'^_WORDLIST_SHA256 = "[a-f0-9]*"$', re.MULTILINE)
updated, count = pattern.subn(f'_WORDLIST_SHA256 = "{digest}"', source)
if count != 1:
raise RuntimeError("Could not locate _WORDLIST_SHA256 in the module.")
module_path.write_text(updated, encoding="utf-8")
def _sha256_of(path: Path) -> str:
hasher = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(4 * 1024 * 1024), b""):
hasher.update(chunk)
return hasher.hexdigest()
def main() -> int:
parser = argparse.ArgumentParser(description="RegaLabs-TTS Sorani censoring tools")
parser.add_argument("--rehash", action="store_true",
help="Re-sign the current word list into this file")
parser.add_argument("--sign-checkpoint", metavar="PATH",
help="Embed the SHA-256 of a new official checkpoint")
parser.add_argument("--hash-checkpoint", metavar="PATH",
help="Print the SHA-256 of a checkpoint file")
parser.add_argument("--self-test", action="store_true", help="Run self-tests")
args = parser.parse_args()
if args.hash_checkpoint:
print(_sha256_of(Path(args.hash_checkpoint)))
return 0
if args.sign_checkpoint:
digest = _sha256_of(Path(args.sign_checkpoint))
source = Path(__file__).read_text(encoding="utf-8")
pattern = re.compile(r'^EXPECTED_FLOW_SHA256 = "[a-f0-9]*"$', re.MULTILINE)
updated, count = pattern.subn(f'EXPECTED_FLOW_SHA256 = "{digest}"', source)
if count != 1:
raise RuntimeError("Could not locate EXPECTED_FLOW_SHA256.")
Path(__file__).write_text(updated, encoding="utf-8")
print(f"Signed checkpoint {digest}")
return 0
if args.rehash:
_embed_digest(Path(__file__), _wordlist_digest())
print(f"Re-signed word list ({_wordlist_digest()})")
return 0
if args.self_test:
assert censor_text("کێر باشە") == "...... باشە"
assert censor_text("کیرەکە") == "......"
assert censor_text("کیریم و قوز") == "...... و ......"
assert censor_text("كێر") == "......" # Arabic kaf -> Kurdish kaf
assert censor_text("کێرد") == "کێرد" # knife (kêrd), not a match
assert censor_text("کۆنە") == "کۆنە" # old, not a match
assert censor_text("کیرەکانی") == "......"
assert censor_text("قوزەکە") == "......"
assert censor_text("کوزەکانی") == "......"
assert censor_text("گەنە و کۆند") == "...... و ......"
assert censor_text("لەشفرۆشەکە") == "......"
assert censor_text("نێرینەبازی") == "......"
assert censor_text("هەرزەکارەکە") == "......"
assert censor_text("داوێنپیسی") == "......"
assert censor_text("گەن") == "گەن" # rotten, not a match
assert censor_text("هەرزە") == "هەرزە" # nonsense, not a match
verify_wordlist()
original = SORANI_SEX_TERMS
try:
globals()["SORANI_SEX_TERMS"] = original + ("تاقیکردنەوە",)
try:
verify_wordlist()
raise AssertionError("tampered list was not detected")
except CensorIntegrityError:
pass
finally:
globals()["SORANI_SEX_TERMS"] = original
print("censor self-test passed")
return 0
parser.print_help()
return 0
if __name__ == "__main__":
sys.exit(main())
|