Files
nix-config-ryan4yin/hosts/idols-aquamarine/asr.nix
T
ryan4yin | 二花 58d1af8f9d feat(youko): add pull-through cache for two Nix upstreams (#499)
Run nginx on youko as a loopback pull-through HTTP cache for
cache.nixos.org and cache.numtide.com, exposed by Caddy at
https://nix-cache.writefor.fun.

Assisted-by: DeepSeek Harness (deepseek-flash)
2026-11-01 18:27:43 +08:00

114 lines
4.8 KiB
Nix

{
lib,
mylib,
myvars,
pkgs,
pkgs-npu,
...
}:
let
inherit (myvars.networking.hostsAddr.aquamarine) ipv4;
inherit (myvars.ports) aquamarineAsr;
port = aquamarineAsr;
# OpenVINO publishes static IRs for Whisper only, and the NPU compiler accepts
# nothing less: any other ASR family keeps a dynamic time axis and aborts it
# (WORKAROUNDS.md WA-037). Pinned by content because the NPU caches the
# compiled graph; a version bump should be deliberate.
modelFiles = {
"added_tokens.json" = "sha256-PFH2bEwh+eEmlwB48RrnenjHSu6N9gbunauobkZxCOA=";
"config.json" = "sha256-v9ksCXVHqxLLQquugAi+WlmpH9xas5rM4kSJ64o+ioY=";
"generation_config.json" = "sha256-Rhf8ykWK87kaEDFDqqyRnBq2aAtVLXq9EIEbcki9d7Q=";
"merges.txt" = "sha256-LfKZCjleNejfvHUR4IwS1WAY2NBGkeATPl1jsh4VTcY=";
"normalizer.json" = "sha256-vxxQfchyTKnPmQNkDaz7adri8A7e5PIc66EGpzkvJt0=";
"openvino_config.json" = "sha256-naOOiM7Aaa1UaZ1ifxxZ02w2o9+1S14btc/fWDLvrwM=";
"openvino_decoder_model.bin" = "sha256-wGSZHLr8Q4FWfSmXK3AT3CQCbenDJtA+sebk/ESqlZ8=";
"openvino_decoder_model.xml" = "sha256-rrCfr78cDL+EuvMPRnY0NgBYIvrzs2U1m43gqgTwMEc=";
"openvino_detokenizer.bin" = "sha256-8rPEeCWhCJUl/2XAyOSSceHe5ppAGgT8gnrC3lt3ZuQ=";
"openvino_detokenizer.xml" = "sha256-bhBqFPFLB3G0a3lIqZsdgZ/5OyRVt9qPR3Yaudup3FY=";
"openvino_encoder_model.bin" = "sha256-BZCo81+W1XgBxVmQAo2ReCGschAm40t/P1nXVh/JCOY=";
"openvino_encoder_model.xml" = "sha256-YHE9TtOorI7gIOEcRzfsJ20UyrxqCCU3vd8sALps4HA=";
"openvino_tokenizer.bin" = "sha256-rfo9mikg0PMUEhJwqWCrMx7A8Fg4VEu47KqkIigqb9Q=";
"openvino_tokenizer.xml" = "sha256-y6ME57rVR3O50svM+8hQERfs8uPA9PUzF0Kgo8n+7ZM=";
"preprocessor_config.json" = "sha256-ZUzxjT4WO5SM6vl2baVs4LUt4mXVhnPPYck3bxJr1Jk=";
"special_tokens_map.json" = "sha256-uupOoJNy60/KhrTkNGE5/XPLgH1Qh+neCUjpcXOcPnQ=";
"tokenizer_config.json" = "sha256-PHWUDfzjopT8pwQaX6/wEWd/G2j6heR1EbuM9tzK3tY=";
"tokenizer.json" = "sha256-XBvzDJ5xbhR3vt74RrAb4AE9rsuJ6ePveribI8F43xs=";
"vocab.json" = "sha256-Z4jICwgumw0TkxR9Oj5iuhkoWsDIKs6OXvAPN+rViXE=";
};
whisperModel = pkgs.runCommandLocal "whisper-large-v3-turbo-int8-ov" { } ''
mkdir -p $out
${lib.concatStringsSep "\n" (
lib.mapAttrsToList (name: hash: ''
ln -s ${
pkgs.fetchurl {
url = "https://huggingface.co/OpenVINO/whisper-large-v3-turbo-int8-ov/resolve/main/${name}";
inherit hash;
}
} $out/${name}
'') modelFiles
)}
'';
# openvino-genai 2026.4.0.0 fails to compile against the OpenVINO headers in
# nixos-unstable (no matching constructor in continuous_batching); the branch's
# nixpkgs builds it, and the service has to match the NPU stack anyway.
pythonEnv = pkgs-npu.python3.withPackages (ps: [
ps.fastapi
ps.numpy
ps.openvino-genai
# FastAPI's form/file parsing is not part of fastapi itself.
ps.python-multipart
ps.uvicorn
]);
in
{
# vinput on the desktop sends raw PCM here and gets text back; the endpoint is
# the OpenAI transcription API, so any client of that shape works.
systemd.services.whisper-asr = {
description = "OpenAI-compatible Whisper transcription service (Intel NPU)";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
environment = {
WHISPER_MODEL_DIR = "${whisperModel}";
WHISPER_MODEL_NAME = "whisper-large-v3-turbo";
WHISPER_DEVICE = "NPU";
WHISPER_HOST = ipv4;
WHISPER_PORT = toString port;
# Compiling the graph for the NPU takes ~70 s; cache it so restarts and
# reboots reuse the compiled blob instead of paying that again.
OV_CACHE_DIR = "/var/cache/whisper-asr";
# Python block-buffers stdout when it is not a tty, which would hold the
# request log lines back instead of putting them in the journal.
PYTHONUNBUFFERED = "1";
# The stock driver cannot find the NPU compiler; the patched one loads it
# from its own store path.
ZE_ENABLE_ALT_DRIVERS = "${lib.getLib pkgs-npu.intel-npu-driver}/lib/libze_intel_npu.so";
};
# Reads its model, writes its cache, uses the NPU and listens on the LAN.
serviceConfig = mylib.systemdHardening.service // {
ExecStart = "${pythonEnv}/bin/python ${./asr/server.py}";
CacheDirectory = "whisper-asr";
DynamicUser = true;
Restart = "on-failure";
RestartSec = 5;
RestrictAddressFamilies = [
"AF_INET"
"AF_UNIX"
];
# The NPU is /dev/accel/accel0; allow that device class only.
PrivateDevices = false;
DevicePolicy = "closed";
DeviceAllow = [ "char-accel rw" ];
# OpenVINO's oneDNN kernels and libffi closures generate code at runtime.
MemoryDenyWriteExecute = false;
# OpenVINO sizes its thread pools from /proc/cpuinfo and /proc/meminfo.
ProcSubset = "all";
};
};
}