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});
|
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
|
||||||
in
|
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: {
|
packages = forAllSystems (pkgs: {
|
||||||
default = pkgs.callPackage ./wyoming-whisper-rs/package.nix { src = self; };
|
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