at master 11 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 utils, 6 ... 7}: 8 9let 10 cfg = config.services.wyoming.faster-whisper; 11 12 inherit (lib) 13 mapAttrsToList 14 mkOption 15 mkEnableOption 16 mkPackageOption 17 optionals 18 types 19 ; 20 21 inherit (builtins) 22 toString 23 ; 24 25 inherit (utils) 26 escapeSystemdExecArgs 27 ; 28in 29 30{ 31 options.services.wyoming.faster-whisper = with types; { 32 package = mkPackageOption pkgs "wyoming-faster-whisper" { }; 33 34 servers = mkOption { 35 default = { }; 36 description = '' 37 Attribute set of wyoming-faster-whisper instances to spawn. 38 ''; 39 type = attrsOf (submodule { 40 options = { 41 enable = mkEnableOption "Wyoming faster-whisper server"; 42 43 model = mkOption { 44 type = str; 45 default = "tiny-int8"; 46 example = "Systran/faster-distil-whisper-small.en"; 47 # https://github.com/home-assistant/addons/blob/master/whisper/DOCS.md#option-model 48 description = '' 49 Name of the voice model to use. Can also be a HuggingFace model ID or a path to 50 a custom model directory. 51 52 With {option}`useTranformers` enabled, a HuggingFace transformers Whisper model 53 ID from HuggingFace like `openai/whisper-tiny.en` must be used. 54 55 Compressed models (`int8`) are slightly less accurate, but smaller and faster. 56 Distilled models are uncompressed and faster and smaller than non-distilled models. 57 58 Available models: 59 - `tiny-int8` (compressed) 60 - `tiny` 61 - `tiny.en` (English only) 62 - `base-int8` (compressed) 63 - `base` 64 - `base.en` (English only) 65 - `small-int8` (compressed) 66 - `distil-small.en` (distilled, English only) 67 - `small` 68 - `small.en` (English only) 69 - `medium-int8` (compressed) 70 - `distil-medium.en` (distilled, English only) 71 - `medium` 72 - `medium.en` (English only) 73 - `large` 74 - `large-v1` 75 - `distil-large-v2` (distilled, English only) 76 - `large-v2` 77 - `distil-large-v3` (distilled, English only) 78 - `large-v3` 79 - `turbo` (faster than large-v3) 80 ''; 81 }; 82 83 useTransformers = mkOption { 84 type = bool; 85 default = false; 86 description = '' 87 Whether to provide the dependencies to allow using transformer models. 88 ''; 89 }; 90 91 uri = mkOption { 92 type = strMatching "^(tcp|unix)://.*$"; 93 example = "tcp://0.0.0.0:10300"; 94 description = '' 95 URI to bind the wyoming server to. 96 ''; 97 }; 98 99 device = mkOption { 100 # https://opennmt.net/CTranslate2/python/ctranslate2.models.Whisper.html# 101 type = enum [ 102 "cpu" 103 "cuda" 104 "auto" 105 ]; 106 default = "cpu"; 107 description = '' 108 Determines the platform faster-whisper is run on. CPU works everywhere, CUDA requires a compatible NVIDIA GPU. 109 ''; 110 }; 111 112 language = mkOption { 113 type = enum [ 114 # https://github.com/home-assistant/addons/blob/master/whisper/config.yaml#L20 115 "auto" 116 "af" 117 "am" 118 "ar" 119 "as" 120 "az" 121 "ba" 122 "be" 123 "bg" 124 "bn" 125 "bo" 126 "br" 127 "bs" 128 "ca" 129 "cs" 130 "cy" 131 "da" 132 "de" 133 "el" 134 "en" 135 "es" 136 "et" 137 "eu" 138 "fa" 139 "fi" 140 "fo" 141 "fr" 142 "gl" 143 "gu" 144 "ha" 145 "haw" 146 "he" 147 "hi" 148 "hr" 149 "ht" 150 "hu" 151 "hy" 152 "id" 153 "is" 154 "it" 155 "ja" 156 "jw" 157 "ka" 158 "kk" 159 "km" 160 "kn" 161 "ko" 162 "la" 163 "lb" 164 "ln" 165 "lo" 166 "lt" 167 "lv" 168 "mg" 169 "mi" 170 "mk" 171 "ml" 172 "mn" 173 "mr" 174 "ms" 175 "mt" 176 "my" 177 "ne" 178 "nl" 179 "nn" 180 "no" 181 "oc" 182 "pa" 183 "pl" 184 "ps" 185 "pt" 186 "ro" 187 "ru" 188 "sa" 189 "sd" 190 "si" 191 "sk" 192 "sl" 193 "sn" 194 "so" 195 "sq" 196 "sr" 197 "su" 198 "sv" 199 "sw" 200 "ta" 201 "te" 202 "tg" 203 "th" 204 "tk" 205 "tl" 206 "tr" 207 "tt" 208 "uk" 209 "ur" 210 "uz" 211 "vi" 212 "yi" 213 "yue" 214 "yo" 215 "zh" 216 ]; 217 example = "en"; 218 description = '' 219 The language used to to parse words and sentences. 220 ''; 221 }; 222 223 initialPrompt = mkOption { 224 type = nullOr str; 225 default = null; 226 # https://github.com/home-assistant/addons/blob/master/whisper/DOCS.md#option-custom_model_type 227 example = '' 228 The following conversation takes place in the universe of 229 Wizard of Oz. Key terms include 'Yellow Brick Road' (the path 230 to follow), 'Emerald City' (the ultimate goal), and 'Ruby 231 Slippers' (the magical tools to succeed). Keep these in mind as 232 they guide the journey. 233 ''; 234 description = '' 235 Optional text to provide as a prompt for the first window. This can be used to provide, or 236 "prompt-engineer" a context for transcription, e.g. custom vocabularies or proper nouns 237 to make it more likely to predict those word correctly. 238 239 Not supported when the {option}`customModelType` is `transformers`. 240 ''; 241 }; 242 243 beamSize = mkOption { 244 type = ints.unsigned; 245 default = 0; 246 example = 5; 247 description = '' 248 The number of beams to use in beam search. 249 Use `0` to automatically select a value based on the CPU. 250 ''; 251 apply = toString; 252 }; 253 254 extraArgs = mkOption { 255 type = listOf str; 256 default = [ ]; 257 description = '' 258 Extra arguments to pass to the server commandline. 259 ''; 260 }; 261 }; 262 }); 263 }; 264 }; 265 266 config = 267 let 268 inherit (lib) 269 mapAttrs' 270 mkIf 271 nameValuePair 272 ; 273 in 274 mkIf (cfg.servers != { }) { 275 assertions = mapAttrsToList (server: options: { 276 assertion = options.useTransformers -> options.initialPrompt == null; 277 message = "wyoming-faster-whisper/${server}: Transformer models (`useTransformers`) do not currently support an `initialPrompt`."; 278 }) cfg.servers; 279 280 systemd.services = mapAttrs' ( 281 server: options: 282 let 283 finalPackage = cfg.package.overridePythonAttrs (oldAttrs: { 284 dependencies = 285 oldAttrs.dependencies 286 # for transformer model support 287 ++ optionals options.useTransformers oldAttrs.optional-dependencies.transformers; 288 }); 289 in 290 nameValuePair "wyoming-faster-whisper-${server}" { 291 inherit (options) enable; 292 description = "Wyoming faster-whisper server instance ${server}"; 293 wants = [ 294 "network-online.target" 295 ]; 296 after = [ 297 "network-online.target" 298 ]; 299 wantedBy = [ 300 "multi-user.target" 301 ]; 302 # https://github.com/rhasspy/wyoming-faster-whisper/issues/27 303 # https://github.com/NixOS/nixpkgs/issues/429974 304 environment."HF_HOME" = "/tmp"; 305 serviceConfig = { 306 DynamicUser = true; 307 User = "wyoming-faster-whisper"; 308 StateDirectory = [ "wyoming/faster-whisper" ]; 309 # https://github.com/home-assistant/addons/blob/master/whisper/rootfs/etc/s6-overlay/s6-rc.d/whisper/run 310 ExecStart = escapeSystemdExecArgs ( 311 [ 312 (lib.getExe finalPackage) 313 "--data-dir" 314 "/var/lib/wyoming/faster-whisper" 315 "--uri" 316 options.uri 317 "--device" 318 options.device 319 "--model" 320 options.model 321 "--language" 322 options.language 323 "--beam-size" 324 options.beamSize 325 ] 326 ++ lib.optionals options.useTransformers [ 327 "--use-transformers" 328 ] 329 ++ lib.optionals (options.initialPrompt != null) [ 330 "--initial-prompt" 331 options.initialPrompt 332 ] 333 ++ options.extraArgs 334 ); 335 CapabilityBoundingSet = ""; 336 DeviceAllow = 337 if 338 builtins.elem options.device [ 339 "cuda" 340 "auto" 341 ] 342 then 343 [ 344 # https://docs.nvidia.com/dgx/pdf/dgx-os-5-user-guide.pdf 345 "char-nvidia-uvm" 346 "char-nvidia-frontend" 347 "char-nvidia-caps" 348 "char-nvidiactl" 349 ] 350 else 351 ""; 352 DevicePolicy = "closed"; 353 LockPersonality = true; 354 MemoryDenyWriteExecute = true; 355 PrivateUsers = true; 356 ProtectHome = true; 357 ProtectHostname = true; 358 ProtectKernelLogs = true; 359 ProtectKernelModules = true; 360 ProtectKernelTunables = true; 361 ProtectControlGroups = true; 362 ProtectProc = "invisible"; 363 # "all" is required because faster-whisper accesses /proc/cpuinfo to determine cpu capabilities 364 ProcSubset = "all"; 365 RestrictAddressFamilies = [ 366 "AF_INET" 367 "AF_INET6" 368 "AF_UNIX" 369 ]; 370 RestrictNamespaces = true; 371 RestrictRealtime = true; 372 SystemCallArchitectures = "native"; 373 SystemCallFilter = [ 374 "@system-service" 375 "~@privileged" 376 ]; 377 UMask = "0077"; 378 }; 379 } 380 ) cfg.servers; 381 }; 382}