feat(hass): use fork of wyoming-oww
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-09-12 18:21:31 -04:00
parent dd4226e10f
commit de8b7fd2bb
5 changed files with 144 additions and 150 deletions

View file

@ -16,21 +16,6 @@
"wyoming"
];
customComponents = builtins.attrValues {
# Switched to HASS Ollama integration
# inherit (self.legacyPackages.${pkgs.system}.hass-components) home-llm;
# Home-llm prompt:
/*
You are 'Homie', a helpful AI Assistant that controls the devices in a house. Complete the following task as instructed.
The current time and date is {{ (as_timestamp(now()) | timestamp_custom("%I:%M %p on %A %B %d, %Y", "EST")) }}.
Services: {{ formatted_tools }}
Devices:
{{ formatted_devices }}
*/
};
config = {
assist_pipeline = {};
conversation = {};
@ -48,12 +33,13 @@
speaker = 0;
};
openwakeword-docker = {
openwakeword = {
enable = true;
uri = "127.0.0.1:10400";
uri = "tcp://127.0.0.1:10400";
threshold = 0.8;
customModelsDirectories = ["${wakewords-src}/en/yo_homie"];
preloadModels = ["yo_homie"];
};
};

View file

@ -7,157 +7,69 @@
inherit
(lib)
attrNames
escapeShellArgs
flatten
concatMapStringsSep
concatStringsSep
filterAttrs
hasAttr
listToAttrs
map
mkEnableOption
mkForce
mkIf
mkOption
mkOverride
nameValuePair
optionals
splitString
types
optionalString
;
flatMap = f: list: flatten (map f list);
cfg = config.services.wyoming.openwakeword-docker;
cfg = config.services.wyoming;
in {
options.services.wyoming.openwakeword-docker = {
enable = mkEnableOption "Wyoming openWakeWord server";
config = let
forkedPkg = import ./pkgs/wyoming-openwakeword.nix pkgs;
image = mkOption {
type = types.package;
default = pkgs.dockerTools.pullImage {
imageName = "docker.io/rhasspy/wyoming-openwakeword";
imageDigest = "sha256:88df83cfdaa5a0dd068f79662d06b81479ec7b59a4bea59751ff5d6f68bad24a";
sha256 = "1c2yhrhhj1wpd5bcc3zaz1gv8mw8dw5m76cjf42nhf2sgwp2hsjl";
finalImageName = "docker.io/rhasspy/wyoming-openwakeword";
finalImageTag = "latest";
};
description = ''
The image that docker will use.
'';
};
uri = mkOption {
type = types.str;
default = "0.0.0.0:10400";
example = "192.0.2.1:5000";
description = ''
URI to bind the wyoming server to.
'';
};
customModelsDirectories = mkOption {
type = types.listOf types.path;
default = [];
description = ''
Paths to directories with custom wake word models (*.tflite model files).
'';
};
preloadModels = mkOption {
type = with types; listOf str;
default = [
"ok_nabu"
];
example = [
# wyoming_openwakeword/models/*.tflite
"alexa"
"hey_jarvis"
"hey_mycroft"
"hey_rhasspy"
"ok_nabu"
];
description = ''
List of wake word models to preload after startup.
'';
};
threshold = mkOption {
type = types.float;
default = 0.5;
description = ''
Activation threshold (0-1), where higher means fewer activations.
See trigger level for the relationship between activations and
wake word detections.
'';
apply = toString;
};
triggerLevel = mkOption {
type = types.int;
default = 1;
description = ''
Number of activations before a detection is registered.
A higher trigger level means fewer detections.
'';
apply = toString;
};
extraArgs = mkOption {
type = with types; listOf str;
default = [];
description = ''
Extra arguments to pass to the server commandline.
'';
apply = escapeShellArgs;
};
};
config = {
assertions = [
{
assertion =
(cfg.enable
&& hasAttr "khepri" config
&& hasAttr "rwDataDir" config.khepri)
|| !cfg.enable;
message = ''
The module `docker` from this same flake is needed to use
this openwakeword implementation.
'';
}
];
systemd.services = let
unitNames = attrNames (
filterAttrs (_: v: v.device == "cpu") config.services.wyoming.faster-whisper.servers
);
in
listToAttrs (map (x:
whisperUnitNames = attrNames (
filterAttrs (_: v: v.device == "cpu") cfg.faster-whisper.servers
);
in {
systemd.services =
# https://github.com/felschr/nixos-config/blob/6a0f0bf76e3ae80c1e180ba6f6c7fd3b8e91d2d3/services/home-assistant/wyoming.nix#L29
mkIf (cfg.faster-whisper.servers != {})
(listToAttrs (map (x:
nameValuePair "wyoming-faster-whisper-${x}" {
serviceConfig.ProcSubset = mkForce "all";
})
unitNames);
whisperUnitNames))
#
# openWakeWord
// mkIf (cfg.openwakeword.enable) {
wyoming-openwakeword.serviceConfig = {
MemoryDenyWriteExecute = mkForce (cfg.openwakeword.package != forkedPkg);
khepri = mkIf cfg.enable {
compositions."openwakeword" = {
networks.default = {};
# changes according to https://github.com/rhasspy/wyoming-openwakeword/pull/27
ExecStart = mkForce (concatStringsSep " " [
"${cfg.openwakeword.package}/bin/wyoming-openwakeword"
services."openwakeword" = {
image = cfg.image;
restart = "always";
networks = ["default"];
"--uri ${cfg.openwakeword.uri}"
"--threshold ${cfg.openwakeword.threshold}"
volumes = map (dir: "${toString dir}:${toString dir}") cfg.customModelsDirectories;
cmd =
(flatMap (model: ["--preload-model" model]) cfg.preloadModels)
++ (flatMap (dir: ["--custom-model-dir" (toString dir)]) cfg.customModelsDirectories)
++ ["--threshold" cfg.threshold]
++ ["--trigger-level" cfg.triggerLevel]
++ optionals (cfg.extraArgs != "") (splitString " " cfg.extraArgs);
(concatMapStringsSep " "
(dir: "--custom-model-dir ${toString dir}")
cfg.openwakeword.customModelsDirectories)
ports = ["${cfg.uri}:10400"];
# removed option https://github.com/rhasspy/wyoming-openwakeword/pull/27#issuecomment-2211822998
(optionalString
(cfg.openwakeword.package != forkedPkg)
(concatMapStringsSep " " (model: "--preload-model ${model}") cfg.openwakeword.preloadModels))
# removed option since preloading was removed
(optionalString
(cfg.openwakeword.package != forkedPkg)
"--trigger-level ${cfg.openwakeword.triggerLevel}")
"${cfg.openwakeword.extraArgs}"
]);
};
};
services.wyoming.openwakeword = mkIf (cfg.openwakeword.enable) {
package = mkOverride 900 forkedPkg;
};
};
}

View file

@ -0,0 +1,43 @@
{
fetchFromGitHub,
fetchpatch,
python3Packages,
speexdsp-ns,
...
}:
python3Packages.buildPythonApplication rec {
pname = "openwakeword";
version = "0.6.0";
pyproject = true;
src = fetchFromGitHub {
owner = "dscripka";
repo = "openWakeWord";
rev = "v${version}";
hash = "sha256-QsXV9REAHdP0Y0fVZuU+Gt9+gcPMB60bc3DOMDYuaDM=";
};
# Patch upstream to enable use of full tensorflow dep
pythonRemoveDeps = ["tflite-runtime"];
patches = [
(fetchpatch {
url = "https://github.com/dscripka/openWakeWord/pull/178/commits/99cd87e8898348255e864540e43bab17ce0576d6.patch";
hash = "sha256-xveMBZTcYfT8LKiiStqYjjOdUOM/v4taQzSewo97Bfc=";
})
];
nativeBuildInputs = with python3Packages; [
setuptools
];
propagatedBuildInputs =
(with python3Packages; [
onnxruntime
tensorflow-bin
tqdm
scipy
scikit-learn
requests
])
++ [speexdsp-ns];
}

View file

@ -0,0 +1,21 @@
{
fetchFromGitHub,
python3Packages,
speexdsp,
swig,
...
}:
python3Packages.buildPythonApplication {
pname = "speexdsp-ns";
version = "0.1.2";
src = fetchFromGitHub {
owner = "TeaPoly";
repo = "speexdsp-ns-python";
rev = "8af784a230e23f4eeaa4a58111774ad0864b1f0b";
hash = "sha256-9IGhHZBlDYfGygB+fAdEDp7qeIEOWBsiLZAUFTVBxG0=";
};
nativeBuildInputs = [swig];
propagatedBuildInputs = [speexdsp];
}

View file

@ -0,0 +1,32 @@
/*
This package uses a `wyoming-openwakeword` fork that makes use of
the upstream `openwakeword` instead of a fork: https://github.com/rhasspy/wyoming-openwakeword/pull/27
It also enforces the python version to 3.11, because tensorflow
cannot be used with 3.12 yet.
*/
pkgs: let
pyPkgs = pkgs.python311Packages;
speexdsp-ns = pkgs.callPackage ./speexdsp-ns.nix {
python3Packages = pyPkgs;
};
openwakeword = pkgs.callPackage ./openwakeword.nix {
inherit speexdsp-ns;
python3Packages = pyPkgs;
};
in
(pkgs.wyoming-openwakeword.override {
python3Packages = pyPkgs;
})
.overrideAttrs (o: {
src = pkgs.fetchFromGitHub {
owner = "rhasspy";
repo = "wyoming-openwakeword";
rev = "synesthesiam-20240627-openwakeword";
hash = "sha256-69oR2LHiUfx8j39nWp7XhG5xTvmOoPCLjSlH1CFvavo=";
};
propagatedBuildInputs = [openwakeword pyPkgs.wyoming];
})