From 25ee286ff75a462c418f920e2a519a6959246e75 Mon Sep 17 00:00:00 2001 From: Adrian G L Date: Thu, 2 Apr 2026 12:19:20 +0200 Subject: [PATCH] feat: add chatterbox-tts-api package and authelia session secret --- flake.nix | 1 + modules/authelia.nix | 7 + packages/chatterbox-tts-api/default.nix | 191 ++++++++++++++++++++++++ secrets/secrets.yaml | 7 +- 4 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 packages/chatterbox-tts-api/default.nix diff --git a/flake.nix b/flake.nix index 38716f2..1f0f815 100644 --- a/flake.nix +++ b/flake.nix @@ -86,6 +86,7 @@ z-image-models = pkgs.callPackage ./packages/z-image-models { }; whisper-models = pkgs.callPackage ./packages/whisper-models { }; fish-speech-models = pkgs.callPackage ./packages/fish-speech-models { }; + chatterbox-tts-api = pkgs.callPackage ./packages/chatterbox-tts-api { }; }; # legolas diff --git a/modules/authelia.nix b/modules/authelia.nix index 3ff1a48..cebb839 100644 --- a/modules/authelia.nix +++ b/modules/authelia.nix @@ -20,11 +20,17 @@ group = "authelia-main"; mode = "0400"; }; + sops.secrets."authelia/sessionSecretFile" = { + owner = "authelia-main"; + group = "authelia-main"; + mode = "0400"; + }; services.authelia.instances.main = { enable = true; secrets.storageEncryptionKeyFile = config.sops.secrets."authelia/storageEncryptionKeyFile".path; secrets.jwtSecretFile = config.sops.secrets."authelia/jwtSecretFile".path; + secrets.sessionSecretFile = config.sops.secrets."authelia/sessionSecretFile".path; settings = { theme = "dark"; @@ -42,6 +48,7 @@ cookies = [ { domain = "lauterer.it"; + authelia_url = "https://authelia.lauterer.it"; } ]; }; diff --git a/packages/chatterbox-tts-api/default.nix b/packages/chatterbox-tts-api/default.nix new file mode 100644 index 0000000..e9d8c9f --- /dev/null +++ b/packages/chatterbox-tts-api/default.nix @@ -0,0 +1,191 @@ +{ + lib, + stdenv, + fetchFromGitHub, + fetchPypi, + python311, + ffmpeg, + makeWrapper, +}: + +let + py = python311; + ps = py.pkgs; + + s3tokenizer = ps.buildPythonPackage rec { + pname = "s3tokenizer"; + version = "0.3.0"; + pyproject = true; + build-system = with ps; [ setuptools ]; + src = fetchPypi { + inherit pname version; + hash = "sha256-eGpf+LXKAjUH4Kaox3k6aqGxVQpz12doUdG4yLEoicU="; + }; + postPatch = "sed -i '/\"pre-commit\"/d' pyproject.toml"; + propagatedBuildInputs = with ps; [ + numpy + torch + torchaudio + onnx + tqdm + einops + ]; + }; + + conformer-pkg = ps.buildPythonPackage rec { + pname = "conformer"; + version = "0.3.2"; + pyproject = true; + build-system = with ps; [ setuptools ]; + src = fetchPypi { + inherit pname version; + hash = "sha256-Mu80+kYf8y4cMwYQJcD1g4hPGdLwq6IAI09Odx93fto="; + }; + propagatedBuildInputs = with ps; [ + einops + torch + ]; + }; + + resemble-perth = ps.buildPythonPackage rec { + pname = "resemble-perth"; + version = "1.0.1"; + pyproject = true; + build-system = with ps; [ setuptools ]; + src = builtins.fetchurl { + url = "https://files.pythonhosted.org/packages/d8/02/f9b5adba306b5cd8bc5525f6bcad8f9d6840c6635d74fcf229a45c0dfb56/resemble_perth-1.0.1.tar.gz"; + sha256 = "14i3mlrjjjkrsv7zmyrppjfc2jkfqar7x63vav72lyz04by8qs74"; + }; + }; + + chatterbox-tts = ps.buildPythonPackage rec { + pname = "chatterbox-tts"; + version = "0.1.4"; + pyproject = true; + build-system = with ps; [ setuptools ]; + src = fetchFromGitHub { + owner = "travisvn"; + repo = "chatterbox-multilingual"; + rev = "exp"; + hash = "sha256-twH9Ucl/NlzFoe8PMHAhbjmI5ma6S2UEs5kfmGKSqgs="; + }; + postPatch = '' + sed -i '/\"gradio\"/d' pyproject.toml + sed -i 's/numpy>=1.24.0,<1.26.0/numpy/' pyproject.toml + sed -i 's/torch==2.6.0/torch/' pyproject.toml + sed -i 's/torchaudio==2.6.0/torchaudio/' pyproject.toml + sed -i 's/transformers==4.46.3/transformers/' pyproject.toml + sed -i 's/diffusers==0.29.0/diffusers/' pyproject.toml + sed -i 's/safetensors==0.5.3/safetensors/' pyproject.toml + sed -i 's/pykakasi==2.3.0/pykakasi/' pyproject.toml + sed -i 's/librosa==0.11.0/librosa/' pyproject.toml + sed -i 's/resemble-perth==1.0.1/resemble-perth/' pyproject.toml + sed -i 's/conformer==0.3.2/conformer/' pyproject.toml + ''; + propagatedBuildInputs = with ps; [ + numpy + librosa + torch + torchaudio + transformers + diffusers + safetensors + pykakasi + s3tokenizer + conformer-pkg + resemble-perth + ]; + }; + + apiSrc = fetchFromGitHub { + owner = "travisvn"; + repo = "chatterbox-tts-api"; + rev = "a5f466128e4baa8e4cceb3bba9b7ca9de6f7ec6b"; + hash = "sha256-yHrUZsmtCjxlEsajbYU3fQpTSepAXJiOfv8cEiolfV4="; + }; + + pythonEnv = py.withPackages (_: [ + chatterbox-tts + ps.fastapi + ps.uvicorn + ps.pydantic + ps.python-multipart + ps.sse-starlette + ps.python-dotenv + ps.psutil + ps.requests + ps.pydub + ]); + +in +stdenv.mkDerivation { + pname = "chatterbox-tts-api"; + version = "2.1.0"; + + src = apiSrc; + + nativeBuildInputs = [ makeWrapper ]; + + buildCommand = '' + mkdir -p $out/bin $out/lib/chatterbox + + cp -r ${apiSrc}/app $out/lib/chatterbox/ + chmod -R u+w $out/lib/chatterbox + cp ${apiSrc}/main.py $out/lib/chatterbox/ + cp ${apiSrc}/voice-sample.mp3 $out/lib/chatterbox/ + + cat > $out/bin/chatterbox-tts-api << 'PYEOF' + #!${pythonEnv.interpreter} + import sys, os + + state_dir = os.environ.get("STATE_DIR", "/var/lib/chatterbox-tts") + model_cache = os.path.join(state_dir, "models") + voice_library = os.path.join(state_dir, "voices") + long_text_data = os.path.join(state_dir, "long-text-jobs") + voice_sample = os.environ.get("VOICE_SAMPLE_PATH", os.path.join(state_dir, "voice-sample.mp3")) + + os.makedirs(model_cache, exist_ok=True) + os.makedirs(voice_library, exist_ok=True) + os.makedirs(long_text_data, exist_ok=True) + + os.environ.setdefault("MODEL_CACHE_DIR", model_cache) + os.environ.setdefault("VOICE_LIBRARY_DIR", voice_library) + os.environ.setdefault("LONG_TEXT_DATA_DIR", long_text_data) + os.environ.setdefault("VOICE_SAMPLE_PATH", voice_sample) + + app_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lib", "chatterbox") + sys.path.insert(0, app_dir) + os.chdir(app_dir) + + import uvicorn + from app.config import Config + + Config.validate() + + host = Config.HOST + port = Config.PORT + + args = sys.argv[1:] + for i, a in enumerate(args): + if a == "--host" and i + 1 < len(args): + host = args[i + 1] + if a == "--port" and i + 1 < len(args): + port = int(args[i + 1]) + + uvicorn.run("app.main:app", host=host, port=port, reload=False, access_log=True) + PYEOF + chmod +x $out/bin/chatterbox-tts-api + + wrapProgram $out/bin/chatterbox-tts-api \ + --prefix PATH : ${lib.makeBinPath [ ffmpeg ]} \ + --prefix LD_LIBRARY_PATH : "${ps.torch}/lib" + ''; + + meta = with lib; { + description = "REST API for Chatterbox TTS with OpenAI compatibility"; + homepage = "https://github.com/travisvn/chatterbox-tts-api"; + license = licenses.agpl3Only; + mainProgram = "chatterbox-tts-api"; + platforms = platforms.linux; + }; +} diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index 735f097..c704436 100644 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -25,6 +25,7 @@ librechat: authelia: storageEncryptionKeyFile: ENC[AES256_GCM,data:zP2i8Ni6MqHpAJeVdcxr6V0eCXobcgbTyu6cDxsi4x4eG2HIFv7waxsCsa+erQgOf5g8+T5c7kIOa99Z5+Zq3kLAhGrIMqtZxn44oemw5Wl2U4ION2yZTdo/C8otpZMqu9rC9l+k4K3XiKN1Aqhyglx9TXNG6FgS8ygx5aBIBwUM,iv:spQdJ3otiZynCleiCG+u3mk/K3axKrfNtSOCzCGnnWQ=,tag:bMbjwOMCxi/+t+x0Xy0jnQ==,type:str] jwtSecretFile: ENC[AES256_GCM,data:gVRyazB5RZ0fVrZ5/8eUuvJjdPBxjQg0vOrhXvgnv07sawti5Wj350UPBlBKthlvya8V6gZdBSl+Aj1nllP1Fl1tC8hDYb93ZmJdHo6CTicsu9lkMvWWfLe112Dhuptbg5AQAlWLu5TpjSGMT4UfXpLlKYdrzaDnIcWBAVn8k9lN,iv:hcHrAK/squwRyXQCx8pJXxVpq+KtcRwCqJ1NQpHpnL0=,tag:eQdM0gzYNw3/TfDBJYrkdg==,type:str] + sessionSecretFile: ENC[AES256_GCM,data:3Cd1DHLrqG1ljMcD10o9nqrkc5aCRrJ6hhvdmN5fIU5xeLN9veTYS6q1qOiIRXIuMoP52OgaEv1J+khVf/fodfk=,iv:14WZD7+S07LTZhMgHG8mjvE/8wdeWNyG+s5n8ZPfo/k=,tag:wFDW9wthWJvgzEg8a1qASg==,type:str] usersFile: ENC[AES256_GCM,data:uJ03GLDPWWCeTV/FQNdkLfpQiG4FeoP5LnfuW8isHDT2dYhTnDZ7bTb3kTH0lps+79mUF5puaX3XrUO0J1cUV3EjkJkgH/FMnQ7D2mA2jJBCjmvnVerwRDtNJXiwtoM7a5N6RQl9stwDCZE7ODGs9YIqg//HQME73K+l4Hp/thA08GKG/ionT+f7ljlM+yL++guNtp/l5dPZS8/OXfTMBL9jtLlG7AmXbE9hoWcdqGK3OLxGWGdzrxkdQByvDrIxYu9i77o+NMRx0JU1LN8UpMQAYVqmBnbln/zNj5m5iuoa5cwpTKvG5rI=,iv:Iwz5tiUZ8Hr4ywjdkEXvA5cl5TZeyz24BVzMmm8q1vg=,tag:PdXguz6B7cpvUjzzMRlsTA==,type:str] vaultwarden: environmentFile: ENC[AES256_GCM,data:HUFCO4di1hSEMitCSGy5wiDNPZ858NIlW/BnaxrFkE4Tws9RwvmhJ+l89/w7A0VGHAp3yNC+t1GUHgNadA15/Ymr7qL8Zby6o69CqZ3tFnMFmBJ9BL3ni0v1E/4iN5YFInMpmM+c8FjlGfTU2nRRu0WUOGR+5s7C8YSGILrDR+jr98YOKuTfiEKqvsGg8o0dc95CjhEtejeaVGimt9f+bWA54BPGkCT1HzD5boAoTwVbD4sxKDP5l5SnBC+mpzX3sECFkoE5E7SnQQEhKtrL+IffnMrcA3nG6AAyAGODuYZ7VNYQ3zNWBhR2nP0ospDelxaTu9aH6IMgMn4h4Y9LU+nCSHkJGykxGt4W6S8DnHxiaqJCpYwAeOaWFlItlsqlMSjYR2nB7OgUHIv5HLaC1Heyn9azZc/HQyHEjsDa0EsHX8HnmJ6kr2PkAXno+zCBZHnZvxKDWPfV9QqUPakZX9VwPg724zFFe+4OsFBzRiJLePA3hXdmnKJFJUr8cFj0QC/JXlkG3FbfogrnBE2BV6YH8PBmjLuZCII0St1LwB77YPcZDbNLnXUbbiM5/F+fGy04qg3/97sE7Sk/ZXh/Yv+SyvrqrizhXcnOlaciC0zY6DGirHzPuPAYraCkIpPmvTV+MGOICOg77sLSronoN7QohCw6SEZdPD9wSDpRMcbIs5PjfqlVu0rziyl8D1cbT6pmktrGcPKnzKeCpBJNgKE+EwwnB7Zymfj3xRxLZSl/x/1GPFuFtvDvwcWWBIU3IDEtI1zaH0PXbErtuvrttIYWx0+Il9VbxLpxjYl2NxEwfwdUmhU+O2Xw18h8leyu4hfX6a2BvLzLR7cLmSJdIeWiW7rU+qy+H9AMZ5TIZNgepXcN9TmVfTmoEm3HCzG+WJD66iMkdspkggpOxxyIMXia3ws80Eht+m0lrSM2eU6NFfVXxixEylQsxZJuiTqof7mQ71OlGlwWYyY11w2QZNeWmmZIOWwrH2L4u7Npj0qUxurafWOFr/022Y70BxpYO+CKysYppSjTKhiPLS5/8d0u4R3a30gnf/JigXisE06DiLYV6LYRxd003Q41A9UOUoZ7j+A115GUqwAuyD9mEHsH9e2zb3bF+a4QCQ2EaVTaWLHWN/pKnDTY4tSUnpr2HiBcF/dRYh9hMZ3S9khiG+7mj/8Wj1eHMB0dEkUMlU5HE/3Qgoga0oKmATekv6gFyRJ5FQxNNJH9RMN4h6ej6BxDbs5g2bdNScs3qPahpefJA/ITipe1y3qrsfHP4dgqGEn8oaiQxDnNvf5TwbHoX+kS5rlLi4x42cS9v8Ov9oZV92O3c+EuXwDVKW8FAoJh1wMNkJhHjjSCVmQWG1UQngkl3HPDjuPadhec0dWCxQh7/TIaS2JJXeHrURYSLmUV9RlzhGYJqmCZbw7oLU//EukfD9vjm591gX7U3rhu1aQNioZWdFtoGj88BJdHtSa2pxhJnwhruDrwpLOV7xXEK8FsV0SOXWP+MNAgCCWxL2KoLBqDvbr21x6rXS4a6M0+iYxIEIZRsODwTKW7hf+g7mLdKLTIv8ptJVX+zhCaFOR0JMAm/P+L48FPH9ccEUoNCwlSnl02wC9oxB+bTvIDnE7OB705ezwSi8Okh8V1fiQopRIKSutXVhMN7J+wda3Y39UzdEiVr/Qzf+pVkOY2RBp4pNQ2G4Umb31qbtamxOf5JoPqmXQhFopdFttC+iHNa0b13aj4kQITUX/lo7gzssSHyTABpkD5mBaA4HRGkVDTo2bAwsVttVH399NYXmSoGU8XKoLbtJ3caa38KrxN8czTl+oDMurds2xrWHIG+fU4nHIeRW3HxuTz+1zwuYoZD2L+813VEZ3+fcnH1AjdoZ21Ioi1P496s4v4huLC/NMq6A==,iv:n41XecN53vEw2xzCO+gS46TwH7Qy08Hra2NFJNHTEHg=,tag:4ypcVk6TvJbDoG11A5miCw==,type:str] @@ -55,7 +56,7 @@ sops: TEhuRFBFQUppVjFKL3JKa0ozNmRLcTAKDrrS8mpHoQoZ54VkY+SYbjoE6AS0fLjc uHuFCrUWqQIwfqHXGlXn7EPUweTfwQ7Od+4JeVp1GbgNLIyH5xNN1g== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-02-13T12:56:54Z" - mac: ENC[AES256_GCM,data:9lOwVBwSeWr6q8SLcDUrQi42XaTEKe40a9MfCZZl3q8Dy+P6bbKAHsRv4GxYmodJvYvQxHGbojTejN3jmUTOF+N614ydJzPP4oeBC5Gto5NZ1SPJQV25X/dEk1wXC3LlC5ZsmRhUuZL9uoRuOiKV9+C7nRgVObUd2rKR/4QzHvg=,iv:iCmlAu6a9XQOlQ2/SPGA0Lo8HFwxweT6g5/qOiqUVIQ=,tag:mfEto5hA5ysPhN2rEBwQsg==,type:str] + lastmodified: "2026-04-01T15:23:17Z" + mac: ENC[AES256_GCM,data:O9BC/cFOPWAKeb2k583dwrdciLkM5AUlKvBtUkbvB/wsd/sayal/59yGcZ8HwCwFLJ50iiv6GvPPFmHVkgMiXwKE/KqCQ0WGjibuBAD7nqOUMzqnsx4edVTVRluIcaSfUlmiwutSvAKDqQdun9Alg6iQDRFBZBOiX+EHiUp7UPk=,iv:iRThX2//KbKrNnbk0ONeP8964lNQvCUAtRx7E8R6rVA=,tag:9lm1ma29aprV0I8rm6hY7A==,type:str] unencrypted_suffix: _unencrypted - version: 3.11.0 + version: 3.12.1