Repository navigation
feat(mobile): model catalog for on-device GGUF models - #7431
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe incremental change is correct and self-consistent: the new local Files Reviewed (11 code files + generated assets)
Verification notes: re-read every changed hunk against HEAD Previous Review Summaries (3 snapshots, latest commit e8f9699)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit e8f9699)Status: No Issues Found | Recommendation: Merge Executive SummaryThe incremental SHA-256 change is correct and self-consistent: each catalog transfer's partial file is hashed in 4 MiB chunks, compared to its published lowercase hex hash, and a mismatch removes the partial files and surfaces a dedicated Files Reviewed (8 files)
Verification notes: re-read every changed hunk against HEAD Previous review (commit c839768)Status: No Issues Found | Recommendation: Merge Executive SummaryThe incremental changes resolve all three previously reported findings and introduce no new issues: the Hermes Files Reviewed (4 files)
Verification: re-read every changed hunk against current HEAD; no remaining Previous review (commit 980e08b)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)CRITICAL
SUGGESTION
Files Reviewed (101 files)
Reviewed by deepseek-v4.1-flash · Input: 122.2K · Output: 29.5K · Cached: 2.5M Review guidance: REVIEW.md from base branch |
|
bot: E2E for e8f9699. A local backend, Metro, and an Android 15 emulator ran on a GCE VM. An iOS 26.5 simulator (iPhone 17 Pro) ran on the Mac through SSH tunnels. Both used a fake local account. Result: all scenarios pass on Android and iOS.
SHA-256 step. I timed it with a temporary
Notes
|
|
bot: E2E for 418a5c1: a local backend, Metro and an Android 15 emulator on a GCE VM; an iOS 26.5 simulator (iPhone 17 Pro) on the Mac through SSH tunnels. A fake local account, analytics off. Native SHA-256 (task 1)The hash of a downloaded file runs in the platform SHA-256 over a stream, on an Expo
The numbers come from a dev-only log line around the storage A corrupted file is refused, with both paths that the check covers. In the dev-only run the pinned Qwen2.5 0.5B entry points at an all-zero SHA-256 (a wrong published hash), so the downloaded bytes cannot match: the app hashes the file, compares, deletes it, and shows "The downloaded file doesn't match its published checksum, so it was removed. Try the download again." The row returns to Download model, and no model is listed. A wrong size and an unreadable model file were already covered by tests. Catalog gaps (task 2)
Scenarios
|


















bot: Quick Chat gets a full catalog of on-device GGUF models from Hugging Face.
What changes
/model-catalog, opens from "Browse models" in the Chat backend settings.num_parametersfilter). Pages stay full. This filter is only a first step: Hugging Face can state the parameter count of a draft model in the repository (for example, a 27B repository reports 1.9B).Linkheader for the next page.hf-plus the first 16 hex digits of the SHA-256, so the same bytes never download twice. A file that the pinned catalog already offers keeps its pinned identity.-00001-of-00002.gguf) are not offered, because llama.rn loads one file. A vision repository pairs each quantization with anmmprojfile: the one with the same quantization, else the smallest.Download integrity
modules/kilo-file-hash, givessha256File(uri). iOS streams the file through CryptoKitSHA256withFileHandlein 1 MiB chunks. Android streams it throughMessageDigest(SHA-256) with a 1 MiB buffer. Both run in an ExpoAsyncFunction, so the hash runs on a background thread, not on the JS or main thread.Device fit
expo-devicetotalMemory. Free storage comes fromPaths.availableDiskSpace.Untrusted input
https://huggingface.co/api/models?.Library choices
@shopify/flash-list2.3.3 (the app's list element).SegmentedControlfrom@/components/ui/segmented-control. I did not use@expo/uicommunitysegmented-control, because the app already has one tab element for this concern.expo-device~57.0.2 (already installed). Free storage:expo-file-system~57.0.7 (already installed).@tanstack/react-queryuseInfiniteQueryanduseQuery.zod.modules/kilo-file-hash, about 30 lines per platform) with the platform SHA-256 (CryptoKit on iOS,java.security.MessageDigeston Android). Rejected:@noble/hashes(removed; the pure-JS hash of a 1 GB file took too long),expo-crypto(hashes only data in memory),expo-file-system57 (only MD5 for a file),react-native-quick-crypto(a large Nitro native dependency with no file hash; the bytes still go through JS). The existing on-device model modules are platform-specific (kilo-apple-modelcompiles a stub on older SDKs), so a small separate module is cleaner than a new function in them.@huggingface/hub. The app uses three plain GET endpoints, and the responses still need validation in the app. The package adds a dependency without less code.Checks
pnpm format,pnpm typecheck,pnpm lint,pnpm check:unused,pnpm check:i18n: pass.vitestforhf-catalog.test.ts,device-fit.test.ts,gguf-catalog.test.ts: pass.