Skip to content

Report the progress of reading the weights while loading a model - #551

Open
aleroot wants to merge 2 commits into
ml-explore:mainfrom
aleroot:feature/model-load-progress
Open

aleroot wants to merge 2 commits into
ml-explore:mainfrom
aleroot:feature/model-load-progress

Conversation

@aleroot

@aleroot aleroot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

Adds a model weights progress handler.

Close the circle of ml-explore/mlx-swift#427 -> ml-explore/mlx-c#126 -> ml-explore/mlx#3742

Requires the first mlx-swift release containing ml-explore/mlx-swift#427, which in turn requires mlx v0.32.1

Note

This PR intentionally leaves the mlx-swift dependency at 0.31.6 because there is not yet a tagged mlx-swift release containing MLX.withLoadProgressHandler.

The implementation has been validated against the head of ml-explore/mlx-swift#427 (fdb7f7aa), together with ml-explore/mlx-c#126 and MLX v0.32.1.

Checklist

Put an x in the boxes that apply.

  • I have read the CONTRIBUTING document
  • I have run pre-commit run --all-files to format my code / installed pre-commit prior to committing changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the necessary documentation (if needed)

Loading a model is dominated by reading the weights from disk, but nothing is
reported while that happens: `progressHandler` only covers the download, so an
application showing "loading..." has no way to draw an accurate progress bar,
and a large model can spend tens of seconds there.

Group the progress callbacks of a load in one value, `LoadProgressHandlers`:

    let container = try await LLMModelFactory.shared.loadContainer(
        from: directory, using: tokenizerLoader,
        progress: .weights { progress in
            print(progress.fractionCompleted)
        })

It is built on the scoped progress handler of mlx-swift, so `_load()` and the
model implementations are untouched -- the handler is installed around the load
and the plain `loadArrays(url:)` calls in `loadWeights()` report to it.
`ModelLoadProgressReporter` aggregates the byte progress that MLX reports per
file -- a model is frequently split into several shards, read concurrently --
into a single `Progress` for the whole model, and coalesces the updates, as MLX
reports roughly one per 4MB and the handler typically hops to the main actor.

Loading is lazy, so the weights are read while the model is evaluated at the end
of `loadWeights()`. The weights that `sanitize(weights:metadata:)` drops are
never evaluated, and therefore never read, so the aggregate can legitimately
stop short of the size of the files: completion is published once the load
returns.

The existing `progressHandler` parameter is unchanged and keeps reporting the
download; `progress.download` is called in addition to it when both are given.

Note: requires the scoped `withLoadProgressHandler(_:_:)` of
ml-explore/mlx-swift#427.
@aleroot

aleroot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

This one cannot build correctly before, the dependencies are ready. Sorry I should have created it as a draft probably ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants