mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-03 17:15:19 -07:00
fix(setup): pin dev venv to Python 3.12
This commit is contained in:
committed by
jamiepine
parent
51f49dea19
commit
a14f0ce3d9
@@ -7,6 +7,25 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Developer Experience
|
||||||
|
|
||||||
|
- **`just setup` now pins the Python venv to 3.12.** Setup preferred `python3.12` when
|
||||||
|
it found one but silently fell back to 3.13 or bare `python3`, so a machine on
|
||||||
|
3.13/3.14 built a venv that then failed to install dependencies capped below 3.13
|
||||||
|
(kokoro, misaki, numpy, numba) with `Could not find a version that satisfies the
|
||||||
|
requirement`. Setup now requires 3.12, and recreates a venv that is on the wrong
|
||||||
|
version or whose pip doesn't run, so a stale or half-built one heals on the next
|
||||||
|
`just setup` rather than needing a manual `rm -rf backend/venv`. Reporting 3.12
|
||||||
|
isn't enough for an interpreter to be chosen — it has to actually build a working
|
||||||
|
venv, so setup falls through to the next candidate when one can't (Debian packages
|
||||||
|
`ensurepip` separately as `python3.12-venv`; current Homebrew `[email protected]` bottles
|
||||||
|
carry a `pyexpat` that won't load on macOS 26). If none work it exits with the
|
||||||
|
install command for your platform instead of failing deep inside pip. Windows
|
||||||
|
resolves it through the `py -3.12` launcher, since Windows installers don't create
|
||||||
|
a `python3.12` command.
|
||||||
|
- **The 3.12 requirement is now machine-readable.** `backend/pyproject.toml`
|
||||||
|
declares `requires-python = ">=3.12,<3.13"` rather than an open-ended `>=3.12`,
|
||||||
|
so the constraint the docs describe is the one tooling resolves against.
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
- **ROCm setup works on Linux AMD systems.** Docker ROCm builds now keep PyTorch
|
- **ROCm setup works on Linux AMD systems.** Docker ROCm builds now keep PyTorch
|
||||||
|
|||||||
+7
-5
@@ -18,9 +18,11 @@ Thank you for your interest in contributing to Voicebox! This document provides
|
|||||||
curl -fsSL https://bun.sh/install | bash
|
curl -fsSL https://bun.sh/install | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
- **[Python 3.11+](https://python.org)** - For backend development
|
- **[Python 3.12](https://python.org)** - For backend development. Voicebox pins its
|
||||||
|
virtual environment to 3.12 to match CI, because some dependencies have no 3.13
|
||||||
|
release yet. On Debian/Ubuntu also install `python3.12-venv`.
|
||||||
```bash
|
```bash
|
||||||
python --version # Should be 3.11 or higher
|
python3.12 --version # Should be 3.12.x
|
||||||
```
|
```
|
||||||
|
|
||||||
- **[Rust](https://rustup.rs)** - For Tauri desktop app (installed automatically by Tauri CLI)
|
- **[Rust](https://rustup.rs)** - For Tauri desktop app (installed automatically by Tauri CLI)
|
||||||
@@ -44,7 +46,7 @@ just dev # starts backend + desktop app
|
|||||||
```
|
```
|
||||||
|
|
||||||
`just setup` handles everything automatically, including:
|
`just setup` handles everything automatically, including:
|
||||||
- Creating a Python virtual environment
|
- Creating a Python virtual environment pinned to 3.12 (recreating it if an existing venv is on the wrong version)
|
||||||
- Installing Python dependencies (with CUDA PyTorch on Windows if an NVIDIA GPU is detected)
|
- Installing Python dependencies (with CUDA PyTorch on Windows if an NVIDIA GPU is detected)
|
||||||
- Installing MLX dependencies on Apple Silicon
|
- Installing MLX dependencies on Apple Silicon
|
||||||
- Installing JavaScript dependencies
|
- Installing JavaScript dependencies
|
||||||
@@ -67,7 +69,7 @@ just --list # see all available commands
|
|||||||
|
|
||||||
#### Windows Notes
|
#### Windows Notes
|
||||||
|
|
||||||
The justfile works natively on Windows via PowerShell. No WSL or Git Bash required. On Windows with an NVIDIA GPU, `just setup` automatically installs CUDA-enabled PyTorch for GPU acceleration.
|
The justfile works natively on Windows via PowerShell. No WSL or Git Bash required. On Windows with an NVIDIA GPU, `just setup` automatically installs CUDA-enabled PyTorch for GPU acceleration. Python 3.12 is located via the `py -3.12` launcher; install it with `winget install -e --id Python.Python.3.12` if it's missing.
|
||||||
|
|
||||||
### Model Downloads
|
### Model Downloads
|
||||||
|
|
||||||
@@ -363,7 +365,7 @@ See [docs/content/docs/overview/troubleshooting.mdx](docs/content/docs/overview/
|
|||||||
|
|
||||||
**Quick fixes:**
|
**Quick fixes:**
|
||||||
|
|
||||||
- **Backend won't start:** Check Python version (3.11+), ensure venv is activated, install dependencies
|
- **Backend won't start:** Check Python version (must be 3.12), ensure venv is activated, install dependencies
|
||||||
- **Tauri build fails:** Ensure Rust is installed, clean build with `cd tauri/src-tauri && cargo clean`
|
- **Tauri build fails:** Ensure Rust is installed, clean build with `cd tauri/src-tauri && cargo clean`
|
||||||
- **OpenAPI client generation fails:** Ensure backend is running, check `curl http://localhost:17493/openapi.json`
|
- **OpenAPI client generation fails:** Ensure backend is running, check `curl http://localhost:17493/openapi.json`
|
||||||
|
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ just dev # starts backend + desktop app
|
|||||||
|
|
||||||
Install [just](https://github.com/casey/just): `brew install just` or `cargo install just`. Run `just --list` to see all commands.
|
Install [just](https://github.com/casey/just): `brew install just` or `cargo install just`. Run `just --list` to see all commands.
|
||||||
|
|
||||||
**Prerequisites:** [Bun](https://bun.sh), [Rust](https://rustup.rs), [Python 3.11+](https://python.org), [Tauri Prerequisites](https://v2.tauri.app/start/prerequisites/), and [Xcode](https://developer.apple.com/xcode/) on macOS.
|
**Prerequisites:** [Bun](https://bun.sh), [Rust](https://rustup.rs), [Python 3.12](https://python.org), [Tauri Prerequisites](https://v2.tauri.app/start/prerequisites/), and [Xcode](https://developer.apple.com/xcode/) on macOS.
|
||||||
|
|
||||||
The repo ships a pre-wired `.mcp.json` at the root — running Claude Code inside this checkout picks up the Voicebox MCP tools automatically once the dev app is running.
|
The repo ships a pre-wired `.mcp.json` at the root — running Claude Code inside this checkout picks up the Voicebox MCP tools automatically once the dev app is running.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "voicebox-backend"
|
name = "voicebox-backend"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
requires-python = ">=3.12"
|
# This is the one place the Python pin is explained; the justfile and docs point here
|
||||||
|
# rather than restating it. The upper bound is real, not defensive: requirements.txt
|
||||||
|
# currently pulls in kokoro and misaki (which declare requires-python <3.13) plus
|
||||||
|
# numpy<2 and numba<0.61 (no 3.13 wheels), so `pip install -r requirements.txt` fails
|
||||||
|
# outright on a later interpreter. Recheck those four before raising it.
|
||||||
|
requires-python = ">=3.12,<3.13"
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Ruff – linter + formatter
|
# Ruff – linter + formatter
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ These two layers communicate via HTTP on `localhost:17493`, with the frontend ma
|
|||||||
|
|
||||||
### Tech Stack
|
### Tech Stack
|
||||||
|
|
||||||
- **Framework**: FastAPI (Python 3.11+)
|
- **Framework**: FastAPI (Python 3.12)
|
||||||
- **TTS Engines**: Qwen3-TTS, Qwen CustomVoice, LuxTTS, Chatterbox, Chatterbox Turbo, TADA, Kokoro
|
- **TTS Engines**: Qwen3-TTS, Qwen CustomVoice, LuxTTS, Chatterbox, Chatterbox Turbo, TADA, Kokoro
|
||||||
- **Transcription**: Whisper (PyTorch or MLX-Whisper)
|
- **Transcription**: Whisper (PyTorch or MLX-Whisper)
|
||||||
- **Inference Backends**: MLX (Apple Silicon), PyTorch (CUDA / ROCm / XPU / DirectML / CPU)
|
- **Inference Backends**: MLX (Apple Silicon), PyTorch (CUDA / ROCm / XPU / DirectML / CPU)
|
||||||
|
|||||||
@@ -32,11 +32,12 @@ Ensure you have these installed:
|
|||||||
curl -fsSL https://bun.sh/install | bash
|
curl -fsSL https://bun.sh/install | bash
|
||||||
```
|
```
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Python 3.11+" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>}>
|
<Card title="Python 3.12" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>}>
|
||||||
[Download Python](https://python.org)
|
[Download Python](https://python.org)
|
||||||
```bash
|
```bash
|
||||||
python --version
|
python3.12 --version # Should be 3.12.x
|
||||||
```
|
```
|
||||||
|
Voicebox pins its venv to 3.12 to match CI, because some dependencies have no 3.13 release yet. On Debian/Ubuntu also install `python3.12-venv`.
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Rust" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>}>
|
<Card title="Rust" icon={<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>}>
|
||||||
[Install Rust](https://rustup.rs)
|
[Install Rust](https://rustup.rs)
|
||||||
@@ -65,7 +66,7 @@ Run `just --list` to see all available commands. Highlights:
|
|||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| `just setup` | Full setup (Python venv + JS deps + dev sidecar). Detects Apple Silicon for MLX and NVIDIA/Intel Arc on Windows for accelerated PyTorch. |
|
| `just setup` | Full setup (Python venv + JS deps + dev sidecar). Pins the venv to Python 3.12 (recreating a wrong-version one) and detects Apple Silicon for MLX and NVIDIA/Intel Arc on Windows for accelerated PyTorch. |
|
||||||
| `just setup-python` | Python venv + dependencies only |
|
| `just setup-python` | Python venv + dependencies only |
|
||||||
| `just setup-js` | `bun install` only |
|
| `just setup-js` | `bun install` only |
|
||||||
|
|
||||||
@@ -214,8 +215,8 @@ This installs dependencies for:
|
|||||||
```bash
|
```bash
|
||||||
cd backend
|
cd backend
|
||||||
|
|
||||||
# Create virtual environment
|
# Create virtual environment (use Python 3.12 — see Prerequisites)
|
||||||
python -m venv venv
|
python3.12 -m venv venv
|
||||||
|
|
||||||
# Activate virtual environment
|
# Activate virtual environment
|
||||||
source venv/bin/activate # macOS/Linux
|
source venv/bin/activate # macOS/Linux
|
||||||
@@ -277,7 +278,7 @@ bun run tauri dev
|
|||||||
|
|
||||||
<AccordionGroup>
|
<AccordionGroup>
|
||||||
<Accordion title="Backend won't start">
|
<Accordion title="Backend won't start">
|
||||||
- Check Python version (must be 3.11+)
|
- Check Python version (must be 3.12)
|
||||||
- Ensure virtual environment is activated: `source backend/venv/bin/activate`
|
- Ensure virtual environment is activated: `source backend/venv/bin/activate`
|
||||||
- Verify all dependencies are installed: `pip install -r requirements.txt`
|
- Verify all dependencies are installed: `pip install -r requirements.txt`
|
||||||
- Check if port 17493 is available
|
- Check if port 17493 is available
|
||||||
|
|||||||
@@ -292,13 +292,19 @@ This is expected behavior. The first generation downloads the selected TTS engin
|
|||||||
|
|
||||||
<AccordionGroup>
|
<AccordionGroup>
|
||||||
<Accordion title="Python Version">
|
<Accordion title="Python Version">
|
||||||
Ensure Python 3.11 or higher:
|
Ensure Python 3.12:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python --version
|
python3.12 --version
|
||||||
```
|
```
|
||||||
|
|
||||||
If not, install Python 3.11+ and recreate the virtual environment.
|
Voicebox pins its venv to Python 3.12 because some dependencies have no 3.13
|
||||||
|
release yet. `just setup` recreates a venv that was built on the wrong version,
|
||||||
|
so re-running it is usually the fix.
|
||||||
|
|
||||||
|
If `just setup` can't find a usable 3.12, install one: `uv python install 3.12`
|
||||||
|
works anywhere, or use `brew install [email protected]` on macOS and `sudo apt install
|
||||||
|
python3.12 python3.12-venv` on Debian/Ubuntu.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Virtual Environment">
|
<Accordion title="Virtual Environment">
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ pip := if os() == "windows" { venv_bin / "pip.exe" } else { venv_bin / "pip" }
|
|||||||
# Shell selection: use powershell on Windows, bash elsewhere
|
# Shell selection: use powershell on Windows, bash elsewhere
|
||||||
set windows-shell := ["powershell", "-NoProfile", "-Command"]
|
set windows-shell := ["powershell", "-NoProfile", "-Command"]
|
||||||
|
|
||||||
# Detect best python for venv creation (platform-aware)
|
# Pin the venv to one version instead of following whatever `python3` happens to be;
|
||||||
system_python := if os() == "windows" { "python" } else { `command -v python3.12 2>/dev/null || command -v python3.13 2>/dev/null || echo python3` }
|
# a newer interpreter fails `pip install` with "no matching distribution". See
|
||||||
|
# requires-python in backend/pyproject.toml for which dependencies set the ceiling.
|
||||||
|
# Keep in sync with that and with actions/setup-python in .github/workflows.
|
||||||
|
python_version := "3.12"
|
||||||
|
|
||||||
# ─── Setup ────────────────────────────────────────────────────────────
|
# ─── Setup ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -32,14 +35,66 @@ setup: setup-python setup-js
|
|||||||
setup-python:
|
setup-python:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
if [ ! -d "{{ venv }}" ]; then
|
|
||||||
echo "Creating Python virtual environment..."
|
is_pinned() {
|
||||||
PY_MINOR=$({{ system_python }} -c "import sys; print(sys.version_info[1])")
|
"$1" -c 'import sys; sys.exit(sys.version_info[:2] != tuple(map(int, "{{ python_version }}".split("."))))' 2>/dev/null
|
||||||
if [ "$PY_MINOR" -gt 13 ]; then
|
}
|
||||||
echo "Warning: Python 3.$PY_MINOR detected. ML packages may not be compatible."
|
|
||||||
echo "Recommended: brew install [email protected]"
|
# Testing for an executable file is not enough: console scripts hard-code the
|
||||||
|
# venv's absolute path in their shebang, so a venv that was built elsewhere and
|
||||||
|
# moved into place has a pip that exists, is executable, and still dies with
|
||||||
|
# "cannot execute: required file not found". Running it is the only real check.
|
||||||
|
pip_works() {
|
||||||
|
"{{ pip }}" --version >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Every interpreter on PATH that could be the pinned version, plus uv's, which
|
||||||
|
# keeps its Pythons off PATH. More than one can match, and the first is not
|
||||||
|
# necessarily the good one.
|
||||||
|
candidates() {
|
||||||
|
type -aP "python{{ python_version }}" python3 python 2>/dev/null || true
|
||||||
|
if command -v uv >/dev/null 2>&1; then
|
||||||
|
uv python find "{{ python_version }}" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
{{ system_python }} -m venv {{ venv }}
|
}
|
||||||
|
|
||||||
|
# Reuse the venv only if it is on the pinned version and has a working pip: a
|
||||||
|
# wrong-version venv resolves dependencies against the wrong Python, and a
|
||||||
|
# broken pip means an earlier `-m venv` was interrupted or the venv was moved.
|
||||||
|
if [ -d "{{ venv }}" ] && ! { is_pinned "{{ python }}" && pip_works; }; then
|
||||||
|
echo "Existing venv is not a working Python {{ python_version }} environment — recreating..."
|
||||||
|
rm -rf "{{ venv }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "{{ venv }}" ]; then
|
||||||
|
# Building the venv is itself the test, so there is no separate probe to keep
|
||||||
|
# in sync. An interpreter can report the right version and still fail inside
|
||||||
|
# `-m venv` — Debian packages ensurepip separately, and Homebrew has shipped a
|
||||||
|
# pyexpat that won't dlopen — so fall through to the next candidate rather
|
||||||
|
# than giving up on the first. Their output is held back until every candidate
|
||||||
|
# has failed, so a run that recovers doesn't look like a broken one.
|
||||||
|
log="$(mktemp)"
|
||||||
|
while IFS= read -r py; do
|
||||||
|
is_pinned "$py" || continue
|
||||||
|
echo "Creating Python virtual environment with $py ..."
|
||||||
|
"$py" -m venv "{{ venv }}" >>"$log" 2>&1 && pip_works && break
|
||||||
|
echo " ...it reports {{ python_version }} but cannot build a working venv; trying the next."
|
||||||
|
rm -rf "{{ venv }}"
|
||||||
|
done < <(candidates | sed 's|//*|/|g' | awk '!seen[$0]++')
|
||||||
|
pip_works || cat "$log"
|
||||||
|
rm -f "$log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! pip_works; then
|
||||||
|
echo ""
|
||||||
|
echo "ERROR: Voicebox needs a Python {{ python_version }} that can create virtual"
|
||||||
|
echo " environments (see requires-python in backend/pyproject.toml)."
|
||||||
|
echo " Any: uv python install {{ python_version }}"
|
||||||
|
echo " macOS: brew install python@{{ python_version }}"
|
||||||
|
echo " Debian: sudo apt install python{{ python_version }} python{{ python_version }}-venv"
|
||||||
|
echo ""
|
||||||
|
echo "Then re-run: just setup"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "Installing Python dependencies..."
|
echo "Installing Python dependencies..."
|
||||||
{{ pip }} install --upgrade pip -q
|
{{ pip }} install --upgrade pip -q
|
||||||
@@ -85,13 +140,40 @@ setup-python:
|
|||||||
|
|
||||||
[windows]
|
[windows]
|
||||||
setup-python:
|
setup-python:
|
||||||
if (-not (Test-Path "{{ venv }}")) { \
|
$target = "{{ python_version }}"; \
|
||||||
Write-Host "Creating Python virtual environment..."; \
|
function Test-Py($exe) { \
|
||||||
$pyMinor = & {{ system_python }} -c "import sys; print(sys.version_info[1])"; \
|
if (-not $exe -or -not (Test-Path $exe)) { return $false }; \
|
||||||
if ([int]$pyMinor -gt 13) { \
|
$v = & $exe -c "import sys; print('%s.%s' % sys.version_info[:2])" 2>$null; \
|
||||||
Write-Host "Warning: Python 3.$pyMinor detected. ML packages may not be compatible."; \
|
return ($LASTEXITCODE -eq 0 -and $v -eq $target); \
|
||||||
}; \
|
}; \
|
||||||
& {{ system_python }} -m venv {{ venv }}; \
|
function Test-Pip { \
|
||||||
|
if (-not (Test-Path "{{ pip }}")) { return $false }; \
|
||||||
|
& "{{ pip }}" --version *> $null; \
|
||||||
|
return ($LASTEXITCODE -eq 0); \
|
||||||
|
}; \
|
||||||
|
$py = $null; \
|
||||||
|
if (Get-Command py -ErrorAction SilentlyContinue) { \
|
||||||
|
$p = & py "-$target" -c "import sys; print(sys.executable)" 2>$null; \
|
||||||
|
if ($LASTEXITCODE -eq 0) { $py = $p }; \
|
||||||
|
}; \
|
||||||
|
if (-not $py) { foreach ($n in @("python$target", "python")) { \
|
||||||
|
$c = Get-Command $n -ErrorAction SilentlyContinue; \
|
||||||
|
if ($c -and (Test-Py $c.Source)) { $py = $c.Source; break }; \
|
||||||
|
} }; \
|
||||||
|
if (-not $py) { \
|
||||||
|
Write-Host "ERROR: Voicebox requires Python $target (see requires-python in backend/pyproject.toml)."; \
|
||||||
|
Write-Host "Install it from https://python.org or: winget install -e --id Python.Python.$target"; \
|
||||||
|
Write-Host "Then re-run: just setup"; \
|
||||||
|
exit 1; \
|
||||||
|
}; \
|
||||||
|
if ((Test-Path "{{ venv }}") -and (-not (Test-Py "{{ python }}") -or -not (Test-Pip))) { \
|
||||||
|
Write-Host "Existing venv is not a working Python $target env - recreating..."; \
|
||||||
|
Remove-Item -Recurse -Force "{{ venv }}"; \
|
||||||
|
}; \
|
||||||
|
if (-not (Test-Path "{{ venv }}")) { \
|
||||||
|
Write-Host "Creating Python virtual environment with $py ..."; \
|
||||||
|
& $py -m venv "{{ venv }}"; \
|
||||||
|
if ($LASTEXITCODE -ne 0 -or -not (Test-Pip)) { Write-Host "ERROR: could not create a working venv with $py"; exit 1 }; \
|
||||||
}
|
}
|
||||||
Write-Host "Installing Python dependencies..."
|
Write-Host "Installing Python dependencies..."
|
||||||
& "{{ python }}" -m pip install --upgrade pip -q
|
& "{{ python }}" -m pip install --upgrade pip -q
|
||||||
|
|||||||
Reference in New Issue
Block a user