KittenTTS 0.8.1 requires misaki[en]>=0.9.4. PyPI's misaki 0.9.4 caps Python below 3.13. NousResearch/misaki f03fd2be73 is upstream main with the cap raised to <3.15 and no code change, so the extra declares misaki[en] at that commit and uv resolves KittenTTS's requirement to it. misaki[en] also lists spacy-curated-transformers. The newest release that fits spacy 3.8, 0.3.1, caps Python below 3.14, so PM's `uv pip check` rejected every 3.14 venv that carried it. Nothing imports it: only transformer spaCy models use it, and KittenTTS never builds a misaki G2P. A never-true override marker removes it from the lock. torch, transformers, spaCy and num2words stay: misaki.en imports them at module level and KittenTTS imports misaki.en. Native bundles sync with --all-extras, so un-gating the extra would put KittenTTS, torch and spaCy in every payload. [tool.hermes] opt-in-extras names the extras that only an explicit selection installs. PM adds --no-extra for each of them to an all-extras sync, so bundles and their recorded feature list leave kittentts out, and setup still installs it through sync_venv when the user picks it. The extra is gated on platform instead of Python: onnxruntime has no Intel macOS wheel and torch has no Windows ARM64 wheel. Nix gets the hatchling build backend the misaki git source needs. Verified on macOS arm64 with CPython 3.14.7 and uv 0.12.3: uv sync --frozen --extra kittentts from this lock, then uv pip check reports all 126 packages compatible. _generate_kittentts writes a 3.87 s 24 kHz WAV (RMS 3661), and no spaCy model is downloaded. A dry-run all-extras sync with --no-extra kittentts selects none of kittentts, misaki, torch, transformers or spaCy. The new build test fails without the environment change and passes with it.
182 lines
5.7 KiB
Nix
182 lines
5.7 KiB
Nix
# nix/python.nix — uv2nix virtual environment builder
|
|
{
|
|
lib,
|
|
callPackage,
|
|
uv2nix,
|
|
pyproject-nix,
|
|
pyproject-build-systems,
|
|
stdenv,
|
|
# Filtered Python source (see lib.nix pythonSrc) — keeps JS/docs/skills
|
|
# edits from invalidating the venv derivation.
|
|
pythonSrc,
|
|
dependency-groups ? [ "all" ],
|
|
}:
|
|
let
|
|
# The interpreter family comes from pm/lock.json (pythonLock.nix owns the
|
|
# selection); every override below must be built for THAT interpreter.
|
|
pythonLock = callPackage ./pythonLock.nix { };
|
|
python = pythonLock.interpreter;
|
|
pythonPackages = python.pkgs;
|
|
|
|
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = pythonSrc; };
|
|
hacks = callPackage pyproject-nix.build.hacks { };
|
|
|
|
overlay = workspace.mkPyprojectOverlay {
|
|
sourcePreference = "wheel";
|
|
};
|
|
|
|
isAarch64Darwin = stdenv.hostPlatform.system == "aarch64-darwin";
|
|
|
|
# Keep the workspace locked through uv2nix, but supply the local voice stack
|
|
# from nixpkgs so wheel-only transitive artifacts do not break evaluation.
|
|
mkPrebuiltPassthru = dependencies: {
|
|
inherit dependencies;
|
|
optional-dependencies = { };
|
|
dependency-groups = { };
|
|
};
|
|
|
|
mkPrebuiltOverride =
|
|
final: from: dependencies:
|
|
hacks.nixpkgsPrebuilt {
|
|
inherit from;
|
|
prev = {
|
|
nativeBuildInputs = [ final.pyprojectHook ];
|
|
passthru = mkPrebuiltPassthru dependencies;
|
|
};
|
|
};
|
|
|
|
# Legacy alibabacloud packages ship only sdists with setup.py/setup.cfg
|
|
# and no pyproject.toml, so setuptools isn't declared as a build dep.
|
|
buildSystemOverrides =
|
|
final: prev:
|
|
builtins.mapAttrs
|
|
(
|
|
name: _:
|
|
prev.${name}.overrideAttrs (old: {
|
|
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ final.setuptools ];
|
|
})
|
|
)
|
|
(
|
|
lib.genAttrs [
|
|
"alibabacloud-credentials-api"
|
|
"alibabacloud-endpoint-util"
|
|
"alibabacloud-gateway-dingtalk"
|
|
"alibabacloud-gateway-spi"
|
|
"alibabacloud-tea"
|
|
] (_: null)
|
|
)
|
|
// {
|
|
# The locked sdist has no build-system metadata; setup.py imports
|
|
# setuptools and uses CFFI to compile the bundled libolm.
|
|
python-olm = prev.python-olm.overrideAttrs (old: {
|
|
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ final.resolveBuildSystem {
|
|
setuptools = [ ];
|
|
cffi = [ ];
|
|
};
|
|
});
|
|
# [kittentts] locks misaki as a git source. uv.lock records no build
|
|
# backend for it, so supply the hatchling its pyproject declares.
|
|
misaki = prev.misaki.overrideAttrs (old: {
|
|
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ final.resolveBuildSystem {
|
|
hatchling = [ ];
|
|
};
|
|
});
|
|
};
|
|
|
|
pythonPackageOverrides =
|
|
final: _prev:
|
|
if isAarch64Darwin then
|
|
{
|
|
numpy = mkPrebuiltOverride final pythonPackages.numpy { };
|
|
|
|
pyarrow = mkPrebuiltOverride final pythonPackages.pyarrow { };
|
|
|
|
av = mkPrebuiltOverride final pythonPackages.av { };
|
|
|
|
humanfriendly = mkPrebuiltOverride final pythonPackages.humanfriendly { };
|
|
|
|
coloredlogs = mkPrebuiltOverride final pythonPackages.coloredlogs {
|
|
humanfriendly = [ ];
|
|
};
|
|
|
|
onnxruntime = mkPrebuiltOverride final pythonPackages.onnxruntime {
|
|
coloredlogs = [ ];
|
|
numpy = [ ];
|
|
packaging = [ ];
|
|
};
|
|
|
|
ctranslate2 = mkPrebuiltOverride final pythonPackages.ctranslate2 {
|
|
numpy = [ ];
|
|
pyyaml = [ ];
|
|
};
|
|
|
|
faster-whisper = mkPrebuiltOverride final pythonPackages.faster-whisper {
|
|
av = [ ];
|
|
ctranslate2 = [ ];
|
|
huggingface-hub = [ ];
|
|
onnxruntime = [ ];
|
|
tokenizers = [ ];
|
|
tqdm = [ ];
|
|
};
|
|
}
|
|
else
|
|
{ };
|
|
|
|
pythonSet =
|
|
(callPackage pyproject-nix.build.packages {
|
|
inherit python;
|
|
}).overrideScope
|
|
(
|
|
lib.composeManyExtensions [
|
|
pyproject-build-systems.overlays.default
|
|
overlay
|
|
buildSystemOverrides
|
|
pythonPackageOverrides
|
|
# ``setup.py`` permits wheel/sdist creation only from the sealed
|
|
# Hermes derivation. This is deliberately a derivation environment
|
|
# variable, not a devShell variable: ``nix develop -c uv build``
|
|
# must remain blocked.
|
|
(final: prev: {
|
|
hermes-agent = prev.hermes-agent.overrideAttrs (_old: {
|
|
HERMES_NIX_BUILD = "1";
|
|
});
|
|
})
|
|
]
|
|
);
|
|
|
|
# The editable venv points at the live checkout, so it uses an
|
|
# UNFILTERED workspace rooted at a real path — mkEditablePyprojectOverlay
|
|
# computes relative paths via lib.path.splitRoot, which rejects the
|
|
# filtered pythonSrc (a cleanSourceWith set, not a path). Filtering
|
|
# buys nothing here anyway: the editable install reads from
|
|
# $HERMES_PYTHON_SRC_ROOT at runtime.
|
|
workspaceRoot = ./..;
|
|
editableWorkspace = uv2nix.lib.workspace.loadWorkspace { inherit workspaceRoot; };
|
|
editableOverlay = editableWorkspace.mkEditablePyprojectOverlay {
|
|
root = "$HERMES_PYTHON_SRC_ROOT"; # resolved at shellHook time
|
|
};
|
|
|
|
editableSet = pythonSet.overrideScope (
|
|
lib.composeManyExtensions [
|
|
editableOverlay
|
|
(final: prev: {
|
|
hermes-agent = prev.hermes-agent.overrideAttrs (old: {
|
|
# point straight at the real source instead of the filtered nix store copy
|
|
src = workspaceRoot;
|
|
nativeBuildInputs = old.nativeBuildInputs ++ final.resolveBuildSystem { editables = [ ]; };
|
|
});
|
|
})
|
|
]
|
|
);
|
|
in
|
|
{
|
|
inherit python;
|
|
|
|
venv = pythonSet.mkVirtualEnv "hermes-agent-env" {
|
|
hermes-agent = dependency-groups;
|
|
};
|
|
editableVenv = editableSet.mkVirtualEnv "hermes-agent-editable-env" {
|
|
hermes-agent = dependency-groups;
|
|
};
|
|
}
|