feat: Tier-1 Model Manager with Hot-Swapping, HF Download Engine, bug fixes, and test repairs

- Phase 4: Hugging Face Download Engine — streaming GGUF downloads with progress, cancellation, and managed-model storage
- Phase 5: Model Source Validation — source-mode compatibility, path/file validation, inline error UI with recovery guidance
- Hot-swap: stop runtime → update model_path → restart atomic command
- Model library: folder open, delete model file, local model management
- SettingsPage unified hub: local models list + HF search with 3-state action buttons
- Bug fixes: send button text restored (4 failing tests), reasoning-toggle aria-label, Rust warnings
- All 89 tests passing, cargo check clean
This commit is contained in:
Evandro Fonseca Junior committed 2026-03-31 02:03:55 -03:00
1 parent 01b629cb4a
commit 9a093dca15
23 files changed
+1798 -344

No files matched your search

@@ -0,0 +1,106 @@
# Phase 4: Hugging Face Download Engine
## Context
After completing the unified runtime workspace (Phase 3), the next priority was implementing the Hugging Face download engine. The HF search UI was already functional, but download buttons used a simulated `setInterval` progress animation with no actual file transfer.
**Architecture decision:** Downloads run in Rust (not JavaScript) to avoid IPC bottlenecks for multi-GB GGUF files. Progress is emitted via Tauri v2 events and consumed by a React hook.
**Key constraints:**
- Tauri v2 + React 18 + TypeScript + Vite
- No `@tauri-apps/plugin-fs` configured; only `shell` plugin active
- Existing HF search already uses `reqwest` with optional bearer token
- Managed models directory already resolved via `model_library::paths::managed_models_dir`
## Implementation Summary
### Rust Backend (`src-tauri`)
**`model_library/huggingface.rs`** — Full download engine:
- `HuggingFaceDownloadState` manages active downloads with `AtomicU64` IDs and `Mutex<HashMap>` cancellation flags
- `start_hugging_face_download()` streams bytes via `reqwest` + `tokio::fs::File` to a `.part` temp file
- Emits Tauri events: `started`, throttled `progress` (~500ms), `completed`, `cancelled`, `error`
- `cancel_hugging_face_download()` sets cancellation flag, removes temp file
- Path validation rejects empty, `.`, `..`, and backslash-containing segments
- Direct resolve URL: `https://huggingface.co/{repo}/resolve/main/{fileName}`
- Bearer token applied when available
- Temp file renamed to final destination on success
**`lib.rs`** — Command registration:
- `start_hugging_face_download_command` and `cancel_hugging_face_download_command` registered in `invoke_handler`
- Shared `Arc<HuggingFaceDownloadState>` managed via `.manage()`
**`model_library/types.rs`** — Serializable payloads:
- `HuggingFaceDownloadPhase` enum (Started, Progress, Completed, Cancelled, Error)
- `HuggingFaceDownloadProgress` struct with camelCase JSON fields
**`Cargo.toml`** — Added `tokio` with `fs`, `io-util`, `sync`, `time` features
### Frontend (`tauri-app/src`)
**`features/runtime/types.ts`** — TypeScript types:
- `HuggingFaceDownloadPhase` union type
- `HuggingFaceDownloadProgress` interface matching Rust payload
**`features/runtime/api.ts`** — API bridge:
- `startHuggingFaceDownload(repo, fileName, token)` → `invoke("start_hugging_face_download_command")`
- `cancelHuggingFaceDownload(downloadId)` → `invoke("cancel_hugging_face_download_command")`
- `HUGGING_FACE_DOWNLOAD_EVENT` constant for event listening
**`features/runtime/useModelDownload.ts`** — React hook:
- `useModelDownload(token: string)` returns `isDownloading`, `progress`, `speed`, `downloaded`, `total`, `activeFile`, `error`, `startDownload()`, `cancelDownload()`
- Listens to Tauri events, tracks active download ID, ignores stale events
- Explicit `ModelDownloadSnapshot` interface for correct TypeScript typing
**`features/settings/SettingsPage.tsx`** — UI integration:
- Removed simulated `setInterval` download state
- Imported `useModelDownload` hook
- `.sp-hf-file` class added to each HF file row
- Active file row switches to real progress-bar state when downloading
- Shows real `speed`, `downloaded`, `total`, `progress` values
- Cancel button in active progress state
- Download buttons have visible focus rings and disabled state during active downloads
### Tests
**`features/runtime/useModelDownload.test.tsx`** — Hook unit tests:
- Verifies `startDownload()` invokes correct Tauri command with `{ repo, fileName, token }`
- Verifies progress/speed/downloaded/total update from events
- Verifies stale events are ignored
- Verifies `cancelDownload()` invokes cancel command with active `downloadId`
**`features/settings/SettingsPage.download.test.tsx`** — Integration test:
- Mocks HF search results and `useModelDownload`
- Verifies `.sp-hf-file` row renders and clicking download calls `startDownload("microsoft/phi-4", "quantized/phi-4-q4.gguf")`
**`model_library/huggingface.rs` tests** — Rust unit tests:
- Direct resolve URL construction
- MiB conversion
- Progress percentage calculation
- Path validation (valid and invalid segments)
## Verification
- ✅ `npm run build` — passes
- ✅ `npx tsc --noEmit` — passes (0 errors)
- ✅ `npm test -- useModelDownload.test.tsx SettingsPage.download.test.tsx` — 3/3 tests pass
- ✅ `cargo test hugging_face` — passes
- ⚠️ 6 pre-existing unrelated test failures in `App.test.tsx`, `ChatView.test.tsx`, `MessageBubble.test.tsx` (not caused by this phase)
- ⚠️ 1 pre-existing Rust warning: `save_index` unused in `local_index.rs` (not caused by this phase)
## Files Modified
| File | Change |
|------|--------|
| `tauri-app/src-tauri/Cargo.toml` | Added `tokio` dependency |
| `tauri-app/src-tauri/src/model_library/types.rs` | Added download phase enum + progress struct |
| `tauri-app/src-tauri/src/model_library/huggingface.rs` | Full download engine + tests |
| `tauri-app/src-tauri/src/lib.rs` | Added download commands + managed state |
| `tauri-app/src/features/runtime/types.ts` | Added download types |
| `tauri-app/src/features/runtime/api.ts` | Added download API helpers |
| `tauri-app/src/features/runtime/useModelDownload.ts` | New React hook |
| `tauri-app/src/features/runtime/useModelDownload.test.tsx` | Hook unit tests |
| `tauri-app/src/features/settings/SettingsPage.tsx` | Replaced simulation with real download |
| `tauri-app/src/features/settings/SettingsPage.download.test.tsx` | Integration test |
| `.planning/ROADMAP.md` | Realigned to HF download milestone |
| `.planning/STATE.md` | Updated to Phase 4 complete |