Files
ForgeLM/.planning/phases/04-hf-download-engine/04-CONTEXT.md
Evandro Fonseca Junior 9a093dca15 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
2026-03-31 02:03:55 -03:00

5.5 KiB

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