# hf-fetch-model v0.8.2 — Download Performance & Observability

**Date:** March 22, 2026
**Status:** Proposed
**Context:** Dogfooding hf-fm during anamnesis Phase 2 (downloading 4 GPTQ +
2 AWQ + 4 candidate 8-bit AWQ models) surfaced a critical performance bug and
several UX gaps. The performance bug alone explains observed 50 Mbit/s
throughput on a connection capable of 500 Mbit/s.

---

## 1. auto_plan is computed but never applied (CRITICAL)

**Problem:** The `merge_plan_recommended()` function in `src/download.rs:1050-1120`
correctly computes optimal download settings (concurrency, connections per file,
chunk threshold) based on file size distribution. **But the recommendations are
never applied** because `src/bin/main.rs:356-358` always sets these three fields
from CLI defaults, marking them as `explicit: true` in `ExplicitSettings`. The
`merge_plan_recommended()` function only overrides non-explicit settings — since
all three are always explicit, the plan is dead code.

**Impact:** All downloads use the hardcoded CLI defaults (concurrency=4,
connections_per_file=8, chunk_threshold=100 MiB) regardless of file size
distribution. For repositories with many medium-sized shards, this causes
server rate-limiting on 32 concurrent requests (4 files × 8 connections),
reducing per-connection bandwidth to ~6-7 Mbit/s → ~50 Mbit/s total.

**Root cause:** `src/bin/main.rs` calls `builder.concurrency(args.concurrency)`,
`builder.chunk_threshold(...)`, and `builder.connections_per_file(...)` with
default values, which sets `explicit = true` for all three. The dry-run path
does NOT set these fields, so auto_plan works there — but the actual download
path always overrides.

**Fix:** Make CLI defaults **implicit**. Use `Option<usize>` for the CLI args
and only call the builder methods when the user actually passes the flag:

```rust
// Before (broken): always sets, always explicit
builder = builder.concurrency(args.concurrency);

// After (fixed): only set when user explicitly provides
if let Some(c) = args.concurrency {
    builder = builder.concurrency(c);
}
```

This is a one-file fix in `src/bin/main.rs`. When no flag is provided,
`merge_plan_recommended()` sees `explicit = false` and applies the data-driven
strategy. When the user explicitly passes `--concurrency 4`, their choice is
respected.

**Files:** `src/bin/main.rs` (CLI arg handling), `src/config.rs`
(`ExplicitSettings`)

## 2. Download summary line

**Problem:** After a download completes, the only output is
`Downloaded to: <path>`. No size, no elapsed time, no throughput. For a tool
whose value proposition is speed, this hides the payoff.

**Fix:** Print a summary after the final `Downloaded to:` line:

```
Downloaded to: ~/.cache/huggingface/hub/models--tiiuae--...
  923 MB in 12.3s (75.0 MB/s), 4 files, 8 connections
```

Implementation: accumulate total bytes and start a timer in the download
coordinator; format at completion.

**Files:** `src/download.rs` (download coordinator completion path)

## 3. Progress output in non-TTY contexts

**Problem:** When running in a pipe, background process, or CI, there is zero
output between start and completion. Multi-GB downloads appear to hang.

**Fix:** Detect non-TTY (`!std::io::stdout().is_terminal()`) and emit periodic
one-line progress to stderr:

```
[hf-fm] model.safetensors: 450/923 MB (48%)
```

Interval: every 5 seconds or every 10% of total size, whichever comes first.
No change to TTY behavior (keep the existing progress bar).

**Files:** `src/download.rs` (progress reporting), `src/chunked.rs`
(chunk progress callbacks)

## 4. Redundant `--filter` + `--preset` warning

**Problem:** `hf-fm repo --preset safetensors --filter "*.safetensors"` is
silently redundant. Users don't realize the preset already covers the filter.

**Fix:** When a `--filter` glob is a subset of the active `--preset`, emit:

```
warning: --filter "*.safetensors" is redundant with --preset safetensors
```

Implementation: after resolving the preset into its glob set, check if each
user-supplied `--filter` matches a preset glob.

**Files:** `src/bin/main.rs` (argument validation)

## 5. Search term normalization for quantization queries

**Problem:** Searching for `"AWQ 8bit"`, `"AWQ 8-bit"`, and `"AWQ int8"`
returns different result sets. Common quantization terms are not normalized,
making it hard to find models reliably.

**Fix:** Normalize common quantization synonyms before searching:
- `8bit` = `8-bit` = `int8` = `INT8`
- `4bit` = `4-bit` = `int4` = `INT4`
- `fp8` = `FP8` = `float8`

Implementation: apply synonym expansion to the search query before sending
to the HuggingFace Hub API, or search with multiple variants and deduplicate
results.

**Files:** `src/search.rs` (query preprocessing)

## 6. Search output: download count formatting (minor)

**Problem:** Large download counts use abbreviated formatting (`6.2K`).
Locale-aware formatting (`6,200`) would be clearer.

**Fix:** Optional, low priority. Use thousand-separator formatting for
download counts in `search` output.

**Files:** `src/search.rs` (result formatting)
