Repository navigation
fix: honor VOICEBOX_DATA_DIR in both the backend and the desktop app - #1004
Lvigentini wants to merge 4 commits into
Conversation
backend/README.md documents VOICEBOX_DATA_DIR alongside --data-dir as a way to override the data directory, but config.py never read it -- only VOICEBOX_MODELS_DIR was wired up, so the default stayed hardcoded to ./data. This matters in development: `just dev` starts the backend with a bare `uvicorn backend.main:app`, which never reaches main.py's argparse, so --data-dir is unavailable there and the dev server always writes to ./data inside the repo. Pointing a dev server at the packaged app's data directory meant editing the justfile. Read the variable when computing the default. --data-dir still takes precedence, since it calls set_data_dir() after import. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📝 WalkthroughWalkthrough
ChangesData directory configuration
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/tests/test_data_dir_env.py`:
- Around line 40-41: Restore the original environment before reloading config in
the fixture teardown, so config is reinitialized without the test’s
VOICEBOX_DATA_DIR value. Update the cleanup around importlib.reload(config),
preserving the existing environment-matching behavior for subsequent tests.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ed058dcb-2930-44d2-98de-c5c69210d76d
📒 Files selected for processing (2)
backend/config.pybackend/tests/test_data_dir_env.py
The desktop app ignored VOICEBOX_DATA_DIR entirely. The Tauri shell resolved the data directory from `app.path().app_data_dir()` unconditionally and passed it to the sidecar as an explicit `--data-dir`, which beats the environment variable on the Python side. Reading the variable in backend/config.py is therefore not enough on its own — it only reaches a bare `uvicorn` run, never the packaged app. Resolve the override in the shell instead, before the argument is built. `data_dir_str` feeds all four sidecar spawn sites, so one change covers the plain, ROCm, CUDA and fallback paths. Unset, empty and whitespace-only are all treated as "no override", so a variable cleared to "" falls back to the platform path rather than resolving to the process working directory. Surrounding whitespace is trimmed, since variables set through the Windows GUI often carry a trailing space. The startup log now names the source, and the create_dir_all failure names the path, so a typo in the variable surfaces as an actionable error rather than a silent fallback. Note this also moves `backends/rocm`, `backends/cuda` and `backend_override` under the overridden directory, since they are anchored to the same path. That keeps all app data in one place, which is the point of the variable. Fixes jamiepine#981 Fixes jamiepine#533 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tauri/src-tauri/src/main.rs`:
- Around line 221-225: Update data_dir_override to resolve non-absolute values
against the Tauri process directory before returning them, while preserving
absolute overrides and empty-value handling. Ensure start_server and the
ROCm/CUDA launch paths use this resolved path for directory creation and
--data-dir construction. Add coverage for a relative override asserting the
selected path is absolute and consistent with backend/config.py and
test_data_dir_env.py.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d23711a5-c42f-42b1-9d67-77d9f335765e
📒 Files selected for processing (1)
tauri/src-tauri/src/main.rs
Review catch on the previous commit. data_dir_override returned relative
values unchanged, and the ROCm and CUDA branches spawn the sidecar with
current_dir() set to the backend's onedir folder. A relative --data-dir would
therefore resolve against backends/rocm or backends/cuda rather than where the
user meant — and to a different place than the CPU path, which does not change
the working directory. Two backends could end up with separate data
directories from one setting.
Resolve relative values against the process working directory before the
argument is built. The base is passed in rather than read inside the function,
so the behaviour stays unit-testable without touching the environment.
Also fixes two things in the Python test flagged in review:
- The fixture reloaded config during teardown, which pytest runs *before* it
restores monkeypatch, so the module kept the last test's VOICEBOX_DATA_DIR
for every test that followed. Undo first, then reload.
- The module was imported flat via a sys.path hack, so it reloaded a
different module object than the one the app holds — the app imports
`from ..config import`, i.e. backend.config. Import it the same way the
rest of the suite does, which also drops the sys.path insert.
Rust: 5 unit tests pass, now covering the relative case.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Verified end to end on Windows 11 against a build from this branch. Set
{"healthy": true, "directories": [
{"path": "C:\Users\psyco\test_folder\generations", "exists": true, "writable": true},
{"path": "C:\Users\psyco\test_folder\captures", "exists": true, "writable": true},
{"path": "C:\Users\psyco\test_folder\profiles", "exists": true, "writable": true},
{"path": "C:\Users\psyco\test_folder", "exists": true, "writable": true}]}The directory was populated with Also worth flagging from the same build: Both installers are produced before that point, so it only affects the updater artifacts — unrelated to this PR, but it means a local |
Five findings from the review of jamiepine#1005. Folder assignment on profile creation ------------------------------------- `create_profile` persisted `folder_id` without checking it. The folder-assignment endpoint enforces that a voice only lands in a voice folder, but creation bypassed that entirely — a client could file a new profile straight into a clip folder, or reference a folder that does not exist. Same two checks, raised as ValueError so the route returns 400 like its neighbours. Whitespace-only names --------------------- `" "` passed `min_length=1` because the raw string is three characters long, and the route then stored `.strip()` — an empty label. Same for the duplicate-profile override, which could produce a copy with no name at all. Both now use a `TrimmedName` type that strips first and length-checks the result. Left `AudioChannelUpdate` and `EffectPresetUpdate` alone; they have the same gap but are not this PR's concern. Moved clips lingering in the list --------------------------------- Moving a clip to another folder invalidated the history query but left `allHistory` intact. Past page 0 the refreshed first page is appended rather than replacing the list, so the clip stayed visible in a folder it no longer belonged to. Folder moves now go through one handler that drops back to page 0, which is what makes the next response replace the list. Detaching a folder ------------------ `useDetachFolder` invalidated only the folder tree. Member lists include subfolders, so anyone filtered by the former parent kept seeing the detached child's items until an unrelated refetch. The sixth finding — the flat `config` import in test_data_dir_env — is fixed on the jamiepine#1004 branch and resolves here on merge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0e99d75 to
2c13dde
Compare
Five findings from the review of jamiepine#1005. Folder assignment on profile creation ------------------------------------- `create_profile` persisted `folder_id` without checking it. The folder-assignment endpoint enforces that a voice only lands in a voice folder, but creation bypassed that entirely — a client could file a new profile straight into a clip folder, or reference a folder that does not exist. Same two checks, raised as ValueError so the route returns 400 like its neighbours. Whitespace-only names --------------------- `" "` passed `min_length=1` because the raw string is three characters long, and the route then stored `.strip()` — an empty label. Same for the duplicate-profile override, which could produce a copy with no name at all. Both now use a `TrimmedName` type that strips first and length-checks the result. Left `AudioChannelUpdate` and `EffectPresetUpdate` alone; they have the same gap but are not this PR's concern. Moved clips lingering in the list --------------------------------- Moving a clip to another folder invalidated the history query but left `allHistory` intact. Past page 0 the refreshed first page is appended rather than replacing the list, so the clip stayed visible in a folder it no longer belonged to. Folder moves now go through one handler that drops back to page 0, which is what makes the next response replace the list. Detaching a folder ------------------ `useDetachFolder` invalidated only the folder tree. Member lists include subfolders, so anyone filtered by the former parent kept seeing the detached child's items until an unrelated refetch. The sixth finding — the flat `config` import in test_data_dir_env — is fixed on the jamiepine#1004 branch and resolves here on merge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five findings from the review of jamiepine#1005. Folder assignment on profile creation ------------------------------------- `create_profile` persisted `folder_id` without checking it. The folder-assignment endpoint enforces that a voice only lands in a voice folder, but creation bypassed that entirely — a client could file a new profile straight into a clip folder, or reference a folder that does not exist. Same two checks, raised as ValueError so the route returns 400 like its neighbours. Whitespace-only names --------------------- `" "` passed `min_length=1` because the raw string is three characters long, and the route then stored `.strip()` — an empty label. Same for the duplicate-profile override, which could produce a copy with no name at all. Both now use a `TrimmedName` type that strips first and length-checks the result. Left `AudioChannelUpdate` and `EffectPresetUpdate` alone; they have the same gap but are not this PR's concern. Moved clips lingering in the list --------------------------------- Moving a clip to another folder invalidated the history query but left `allHistory` intact. Past page 0 the refreshed first page is appended rather than replacing the list, so the clip stayed visible in a folder it no longer belonged to. Folder moves now go through one handler that drops back to page 0, which is what makes the next response replace the list. Detaching a folder ------------------ `useDetachFolder` invalidated only the folder tree. Member lists include subfolders, so anyone filtered by the former parent kept seeing the detached child's items until an unrelated refetch. The sixth finding — the flat `config` import in test_data_dir_env — is fixed on the jamiepine#1004 branch and resolves here on merge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
with a little help from opus 5.5
backend/README.mddocumentsVOICEBOX_DATA_DIRalongside--data-diras a way to override the data directory, but it never worked. Two open issues report this: #981 and #533.Why it was broken in two places
Backend.
config.pyonly wired upVOICEBOX_MODELS_DIR; the data directory default stayed hardcoded to./data.Desktop app. Fixing the backend alone is not enough. The Tauri shell resolved the directory from
app.path().app_data_dir()unconditionally and passed it to the sidecar as an explicit--data-dir, which beats the environment variable on the Python side. Both issue reports show it in the log:So the variable could only ever reach a bare
uvicorn backend.main:app, never the packaged app.Changes
backend/config.py— readVOICEBOX_DATA_DIRwhen computing the default.--data-dirstill takes precedence, since it callsset_data_dir()after import. This is also the only override that reachesjust dev, which starts uvicorn directly and never parses CLI arguments.tauri/src-tauri/src/main.rs— resolve the override before the argument is built.data_dir_strfeeds all four sidecar spawn sites, so one change covers the plain, ROCm, CUDA and fallback paths.Unset, empty and whitespace-only all mean "no override", so a variable cleared to
""falls back to the platform path rather than the process working directory. Surrounding whitespace is trimmed, since variables set through the Windows GUI often carry a trailing space.The startup log now names which source won, and the
create_dir_allfailure names the path, so a typo surfaces as an actionable error rather than a silent fallback.Behaviour note
The Tauri
data_diralso anchorsbackends/rocm,backends/cudaand thebackend_overridefile, so those move under the overridden directory too. That seemed right — the point of the variable is to put app data somewhere else — but flagging it in case you'd rather scope the override more narrowly.Tests
backend/tests/test_data_dir_env.pycovers the precedence rules on the Python side.main.rscover the override decision: unset, empty, whitespace-only, verbatim path, and trimming.cargo test --bin voiceboxpasses.No UI changes, so no screenshots apply.
Fixes #981
Fixes #533
Also partially addresses #577
#577 asks for the
GeneratedandCapturedfolder locations to be configurable. Both sit under the data directory, so both move with this change — two of that issue's three asks. What it does not cover is downloading models straight to a chosen location (the issue's first point;VOICEBOX_MODELS_DIRplus the existing migration flow is the current answer), or a folder picker in Settings. Leaving #577 open on that basis rather than claiming it.Refs #577
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
VOICEBOX_DATA_DIRenvironment variable.Bug Fixes
Tests