Add NixOS module, overlay, and systemd service for wyoming-whisper-rs
Add module.nix exposing config.services.wyoming.whisper-cpp with multi-instance support, systemd hardening, and GPU device access. Export as nixosModules.default and overlays.default from the flake. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f346829cc0
commit
7d88c8c865
2 changed files with 349 additions and 0 deletions
|
|
@ -9,6 +9,12 @@
|
|||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
|
||||
in
|
||||
{
|
||||
nixosModules.default = ./wyoming-whisper-rs/module.nix;
|
||||
|
||||
overlays.default = final: prev: {
|
||||
wyoming-whisper-rs = final.callPackage ./wyoming-whisper-rs/package.nix { src = self; };
|
||||
};
|
||||
|
||||
packages = forAllSystems (pkgs: {
|
||||
default = pkgs.callPackage ./wyoming-whisper-rs/package.nix { src = self; };
|
||||
});
|
||||
|
|
|
|||
343
wyoming-whisper-rs/module.nix
Normal file
343
wyoming-whisper-rs/module.nix
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.wyoming.whisper-cpp;
|
||||
|
||||
inherit (lib)
|
||||
mapAttrsToList
|
||||
mkOption
|
||||
mkEnableOption
|
||||
optionals
|
||||
types
|
||||
;
|
||||
|
||||
inherit (builtins)
|
||||
toString
|
||||
;
|
||||
|
||||
inherit (utils)
|
||||
escapeSystemdExecArgs
|
||||
;
|
||||
in
|
||||
|
||||
{
|
||||
options.services.wyoming.whisper-cpp = with types; {
|
||||
package = mkOption {
|
||||
type = package;
|
||||
description = ''
|
||||
The wyoming-whisper-rs package to use.
|
||||
'';
|
||||
};
|
||||
|
||||
servers = mkOption {
|
||||
default = { };
|
||||
description = ''
|
||||
Attribute set of wyoming-whisper-rs instances to spawn.
|
||||
'';
|
||||
type = attrsOf (submodule {
|
||||
options = {
|
||||
enable = mkEnableOption "Wyoming whisper-cpp server";
|
||||
|
||||
model = mkOption {
|
||||
type = path;
|
||||
description = ''
|
||||
Path to the GGML whisper model file.
|
||||
'';
|
||||
example = "/var/lib/wyoming/whisper-cpp/ggml-large-v3-turbo.bin";
|
||||
};
|
||||
|
||||
uri = mkOption {
|
||||
type = strMatching "^(tcp|unix)://.*$";
|
||||
example = "tcp://0.0.0.0:10300";
|
||||
description = ''
|
||||
URI to bind the wyoming server to.
|
||||
'';
|
||||
};
|
||||
|
||||
language = mkOption {
|
||||
type = nullOr (enum [
|
||||
"auto"
|
||||
"af"
|
||||
"am"
|
||||
"ar"
|
||||
"as"
|
||||
"az"
|
||||
"ba"
|
||||
"be"
|
||||
"bg"
|
||||
"bn"
|
||||
"bo"
|
||||
"br"
|
||||
"bs"
|
||||
"ca"
|
||||
"cs"
|
||||
"cy"
|
||||
"da"
|
||||
"de"
|
||||
"el"
|
||||
"en"
|
||||
"es"
|
||||
"et"
|
||||
"eu"
|
||||
"fa"
|
||||
"fi"
|
||||
"fo"
|
||||
"fr"
|
||||
"gl"
|
||||
"gu"
|
||||
"ha"
|
||||
"haw"
|
||||
"he"
|
||||
"hi"
|
||||
"hr"
|
||||
"ht"
|
||||
"hu"
|
||||
"hy"
|
||||
"id"
|
||||
"is"
|
||||
"it"
|
||||
"ja"
|
||||
"jw"
|
||||
"ka"
|
||||
"kk"
|
||||
"km"
|
||||
"kn"
|
||||
"ko"
|
||||
"la"
|
||||
"lb"
|
||||
"ln"
|
||||
"lo"
|
||||
"lt"
|
||||
"lv"
|
||||
"mg"
|
||||
"mi"
|
||||
"mk"
|
||||
"ml"
|
||||
"mn"
|
||||
"mr"
|
||||
"ms"
|
||||
"mt"
|
||||
"my"
|
||||
"ne"
|
||||
"nl"
|
||||
"nn"
|
||||
"no"
|
||||
"oc"
|
||||
"pa"
|
||||
"pl"
|
||||
"ps"
|
||||
"pt"
|
||||
"ro"
|
||||
"ru"
|
||||
"sa"
|
||||
"sd"
|
||||
"si"
|
||||
"sk"
|
||||
"sl"
|
||||
"sn"
|
||||
"so"
|
||||
"sq"
|
||||
"sr"
|
||||
"su"
|
||||
"sv"
|
||||
"sw"
|
||||
"ta"
|
||||
"te"
|
||||
"tg"
|
||||
"th"
|
||||
"tk"
|
||||
"tl"
|
||||
"tr"
|
||||
"tt"
|
||||
"uk"
|
||||
"ur"
|
||||
"uz"
|
||||
"vi"
|
||||
"yi"
|
||||
"yue"
|
||||
"yo"
|
||||
"zh"
|
||||
]);
|
||||
default = null;
|
||||
example = "en";
|
||||
description = ''
|
||||
The language used for speech recognition. Use `null` or `"auto"` for auto-detection.
|
||||
'';
|
||||
};
|
||||
|
||||
beamSize = mkOption {
|
||||
type = ints.positive;
|
||||
default = 5;
|
||||
description = ''
|
||||
The number of beams to use in beam search.
|
||||
'';
|
||||
};
|
||||
|
||||
threads = mkOption {
|
||||
type = ints.unsigned;
|
||||
default = 0;
|
||||
description = ''
|
||||
Number of threads for whisper inference. Use `0` for the whisper default.
|
||||
'';
|
||||
};
|
||||
|
||||
gpuDevice = mkOption {
|
||||
type = ints.unsigned;
|
||||
default = 0;
|
||||
description = ''
|
||||
GPU device index to use.
|
||||
'';
|
||||
};
|
||||
|
||||
noGpu = mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Disable GPU acceleration entirely.
|
||||
'';
|
||||
};
|
||||
|
||||
maxConcurrent = mkOption {
|
||||
type = ints.positive;
|
||||
default = 1;
|
||||
description = ''
|
||||
Maximum number of concurrent transcriptions.
|
||||
'';
|
||||
};
|
||||
|
||||
modelName = mkOption {
|
||||
type = str;
|
||||
default = "whisper";
|
||||
description = ''
|
||||
Model name reported in Wyoming info responses.
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = mkOption {
|
||||
type = listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Extra arguments to pass to the server commandline.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
inherit (lib)
|
||||
mapAttrs'
|
||||
mkIf
|
||||
nameValuePair
|
||||
;
|
||||
|
||||
parseUri = uri:
|
||||
let
|
||||
parts = builtins.match "tcp://([^:]+):(.*)" uri;
|
||||
in
|
||||
if parts != null then {
|
||||
host = builtins.elemAt parts 0;
|
||||
port = builtins.elemAt parts 1;
|
||||
} else null;
|
||||
in
|
||||
mkIf (cfg.servers != { }) {
|
||||
systemd.services = mapAttrs' (
|
||||
server: options:
|
||||
let
|
||||
parsed = parseUri options.uri;
|
||||
in
|
||||
nameValuePair "wyoming-whisper-cpp-${server}" {
|
||||
inherit (options) enable;
|
||||
description = "Wyoming whisper-cpp server instance ${server}";
|
||||
wants = [
|
||||
"network-online.target"
|
||||
];
|
||||
after = [
|
||||
"network-online.target"
|
||||
];
|
||||
wantedBy = [
|
||||
"multi-user.target"
|
||||
];
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
User = "wyoming-whisper-cpp";
|
||||
ExecStart = escapeSystemdExecArgs (
|
||||
[
|
||||
(lib.getExe cfg.package)
|
||||
"--model"
|
||||
(toString options.model)
|
||||
"--host"
|
||||
(if parsed != null then parsed.host else "0.0.0.0")
|
||||
"--port"
|
||||
(if parsed != null then parsed.port else "10300")
|
||||
"--beam-size"
|
||||
(toString options.beamSize)
|
||||
"--threads"
|
||||
(toString options.threads)
|
||||
"--gpu-device"
|
||||
(toString options.gpuDevice)
|
||||
"--max-concurrent"
|
||||
(toString options.maxConcurrent)
|
||||
"--model-name"
|
||||
options.modelName
|
||||
]
|
||||
++ optionals (options.language != null && options.language != "auto") [
|
||||
"--language"
|
||||
options.language
|
||||
]
|
||||
++ optionals options.noGpu [
|
||||
"--no-gpu"
|
||||
]
|
||||
++ options.extraArgs
|
||||
);
|
||||
CapabilityBoundingSet = "";
|
||||
DeviceAllow =
|
||||
if !options.noGpu then
|
||||
[
|
||||
"char-nvidia-uvm"
|
||||
"char-nvidia-frontend"
|
||||
"char-nvidia-caps"
|
||||
"char-nvidiactl"
|
||||
# AMD ROCm
|
||||
"char-drm"
|
||||
"/dev/kfd"
|
||||
]
|
||||
else
|
||||
"";
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "all";
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged"
|
||||
];
|
||||
UMask = "0077";
|
||||
};
|
||||
}
|
||||
) cfg.servers;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue