feat(mediabox): wyoming

This commit is contained in:
arcnmx 2024-09-12 17:15:06 -07:00
parent a5966c0724
commit a202d0703f
9 changed files with 283 additions and 2 deletions

72
modules/nixos/wyoming.nix Normal file
View file

@ -0,0 +1,72 @@
let
serverModule = {
config,
lib,
...
}: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.strings) match toInt;
inherit (lib.lists) elemAt;
in {
options = with lib.types; {
bind = mkOption {
type = str;
readOnly = true;
};
port = mkOption {
type = port;
readOnly = true;
};
};
config = let
matched = match "^tcp://(.*):([0-9]+)$" config.uri;
bind = elemAt matched 0;
port = toInt (elemAt matched 1);
in {
bind = mkIf (matched != null) (mkOptionDefault bind);
port = mkIf (matched != null) (mkOptionDefault port);
};
};
nonServerModule = service: {
config,
lib,
...
} @ args: let
cfg = config.services.wyoming.${service};
module = serverModule (args
// {
name = service;
config = cfg;
});
in {
options.services.wyoming.${service} = module.options;
config.services.wyoming.${service} = module.config;
};
in
{
config,
lib,
...
}: let
inherit (lib.options) mkOption;
inherit (lib.attrsets) genAttrs;
serviceNames = ["piper" "faster-whisper"];
nonServerNames = ["openwakeword" "satellite"];
nonServerServices = map nonServerModule nonServerNames;
in {
imports = nonServerServices;
options.services.wyoming = let
mkServiceOptions = service:
with lib.types; {
servers = mkOption {
type = attrsOf (submodule [serverModule]);
};
};
serverServices = genAttrs serviceNames mkServiceOptions;
in
serverServices
// {
};
}

View file

@ -0,0 +1,60 @@
{
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
wyomingService = {config, ...}: {
nixos = {
serviceAttrPath = ["services" "wyoming" config.name];
assertions = [
(nixosConfig: let
service = nixosConfig.services.wyoming.${config.name};
cfg = service.servers.${config.id} or service;
in {
assertion = (! cfg ? enable) || (config.enable == cfg.enable);
message = "enable mismatch";
})
(mkIf config.enable (nixosConfig: let
service = nixosConfig.services.wyoming.${config.name};
cfg = service.servers.${config.id} or service;
in {
assertion = ! cfg.enable or false || config.ports.default.port == cfg.port or null;
message = "port mismatch";
}))
];
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = {
default = {
transport = "tcp";
};
};
};
in {
config.exports.services = {
faster-whisper = {config, ...}: {
imports = [wyomingService];
displayName = mkAlmostOptionDefault "Wyoming Whisper";
id = mkAlmostOptionDefault "whisper";
ports.default.port = mkAlmostOptionDefault 10300;
};
piper = {config, ...}: {
imports = [wyomingService];
displayName = mkAlmostOptionDefault "Wyoming Piper";
id = mkAlmostOptionDefault "piper";
ports.default.port = mkAlmostOptionDefault 10200;
};
openwakeword = {config, ...}: {
imports = [wyomingService];
displayName = mkAlmostOptionDefault "Wyoming openWakeWord";
ports.default.port = mkAlmostOptionDefault 10400;
};
satellite = {config, ...}: {
imports = [wyomingService];
displayName = mkAlmostOptionDefault "Wyoming Satellite";
ports.default.port = mkAlmostOptionDefault 10700;
};
};
}

View file

@ -0,0 +1,22 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkIf mkDefault;
cfg = config.services.wyoming.openwakeword;
in {
imports = [./wyoming.nix];
services.wyoming.openwakeword = {
enable = mkDefault true;
uri = mkDefault "tcp://0.0.0.0:10400";
# models: https://github.com/dscripka/openWakeWord?tab=readme-ov-file#pre-trained-models
preloadModels = mkDefault [
"ok_nabu"
"hey_rhasspy"
];
};
# allow access to LAN satellites
networking.firewall.interfaces.local.allowedTCPPorts = mkIf cfg.enable [cfg.port];
}

18
nixos/wyoming/piper.nix Normal file
View file

@ -0,0 +1,18 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkDefault;
in {
imports = [./wyoming.nix];
services.wyoming.piper = {
# voices: https://rhasspy.github.io/piper-samples/
servers.piper = {
enable = mkDefault true;
uri = mkDefault "tcp://0.0.0.0:10200";
voice = mkDefault "en_GB-semaine-medium";
speaker = mkDefault 0;
};
};
}

40
nixos/wyoming/whisper.nix Normal file
View file

@ -0,0 +1,40 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkIf mkForce mkDefault;
cfg = config.services.wyoming.faster-whisper;
inherit (cfg.servers) whisper;
useRocm = false; # broken...
in {
imports = [./wyoming.nix];
services.wyoming.faster-whisper = {
# models: https://github.com/rhasspy/wyoming-faster-whisper/releases/tag/v2.0.0
servers.whisper = {
enable = mkDefault true;
language = mkDefault "en";
model = let
#distil = "distil";
#distil = "distil-whisper/distil-whisper";
distil = "Systran/faster-distil-whisper";
#size = "small.en";
size = "medium.en";
#size = "large-v3";
in
mkDefault "${distil}-${size}";
uri = mkDefault "tcp://0.0.0.0:10300";
device = mkIf useRocm "cuda";
};
};
systemd.services.wyoming-faster-whisper-whisper = mkIf whisper.enable {
serviceConfig = mkIf (whisper.device != "cpu" && useRocm) {
DeviceAllow = [
"char-drm"
"char-kfd"
];
SupplementaryGroups = ["render"];
PrivateDevices = mkForce false;
};
};
}

22
nixos/wyoming/wyoming.nix Normal file
View file

@ -0,0 +1,22 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkIf mkMerge;
inherit (lib.attrsets) mapAttrsToList;
cfgs = config.services.wyoming;
in {
config = {
networking.firewall.interfaces.lan.allowedTCPPorts = let
mkServerPort = _: server: mkIf (server.enable && server ? port) server.port;
mkServicePorts = name: cfg:
mapAttrsToList mkServerPort
cfg.servers
or {
${name} = cfg;
};
in
mkMerge (mapAttrsToList mkServicePorts cfgs);
};
}

View file

@ -5,7 +5,7 @@ in {
postPatch =
''
substituteInPlace api/types.go \
--replace 'UseMMap: nil,' 'UseMMap: &[]bool{true}[0],'
--replace-fail 'UseMMap: nil,' 'UseMMap: &[]bool{true}[0],'
''
+ old.postPatch or "";
doCheck = false;
@ -39,4 +39,16 @@ in {
inherit src patches;
};
});
wyoming-openwakeword = let
inherit (prev) wyoming-openwakeword;
drv = prev.wyoming-openwakeword.override {
python3Packages = final.python311Packages;
};
isPython312 = lib.versionAtLeast final.python3Packages.python.version "3.12";
isBroken = wyoming-openwakeword.version == "1.10.0" && isPython312;
in
if isBroken
then drv
else lib.warnIf isPython312 "wyoming-openwakeword override outdated" wyoming-openwakeword;
}

View file

@ -20,6 +20,9 @@ _: {
enable = true;
ports.proxied.enable = true;
};
piper.enable = true;
faster-whisper.enable = true;
openwakeword.enable = true;
cloudflared.enable = true;
plex.enable = true;
invidious.enable = true;

View file

@ -1,5 +1,6 @@
{
config,
gensokyo-zone,
meta,
lib,
pkgs,
@ -16,6 +17,20 @@
"/mnt/Movies".hostPath = kyuuto.libraryDir + "/movies";
"/mnt/Music".hostPath = kyuuto.libraryDir + "/music/assorted";
};
useZLUDA = false;
useRocm = useZLUDA;
rocmPackages =
if useZLUDA
then pkgs.rocmPackages_5
else pkgs.rocmPackages;
zluda = let
rustChannel = gensokyo-zone.inputs.systemd2mqtt.inputs.rust.legacyPackages.x86_64-linux.releases."1.79.0";
in
pkgs.zluda.override {
inherit rocmPackages;
inherit (rustChannel) rustPlatform;
inherit (rustChannel.buildChannel) rustc;
};
in {
imports = let
inherit (meta) nixos;
@ -38,6 +53,11 @@ in {
nixos.mediatomb
nixos.invidious
# accelerated
nixos.wyoming.whisper
nixos.wyoming.piper
nixos.wyoming.openwakeword
# yarr harr fiddle dee dee >w<
nixos.radarr
nixos.sonarr
@ -75,10 +95,22 @@ in {
in [libraryDir];
};
nixpkgs.config = mkIf useZLUDA {
cudaSupport = true;
};
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [mesa.drivers];
extraPackages = with pkgs;
mkMerge [
[mesa.drivers]
(mkIf useZLUDA [zluda])
(mkIf useRocm [rocmPackages.clr.icd rocmPackages.clr])
];
};
environment.systemPackages = with pkgs; [
radeontop
(mkIf useRocm rocmPackages.rocminfo)
];
fileSystems = let
bind = {