Skip to content

KOIYAL/modelshelf

modelshelf

A shared local LLM model registry for desktop apps. Discover every model already on the machine, share one physical copy between apps, keep them up to date — no daemon required.

CI crates.io docs.rs License: MIT OR Apache-2.0 Sponsor

The problem

When several desktop apps each embed a local LLM workflow:

  • Duplicate downloads. App A and App B both pull the same 5 GB model into their own private directory.
  • Stale models. App A silently keeps using a model downloaded a year ago while App B uses a recent one — the user never notices.
  • No visibility. Models are scattered across Ollama's blob store, LM Studio's tree, the Hugging Face cache, and ad-hoc folders. Nothing sees the whole picture.

What modelshelf does

modelshelf is a Rust SDK + CLI built around a simple on-disk convention (~/.modelshelf): a JSON registry, a content-addressed shared model store, and advisory file locks. Any app that links the SDK (or shells out to the CLI) cooperates through that convention — there is no background service to install.

  • Discover — scan Ollama, LM Studio, the Hugging Face hub cache, Jan, GPT4All, and custom directories; list every local model in one unified view, with duplicates detected by content hash.
  • Shareensure() returns an existing local copy (adopted into the shared store via hardlink, zero extra disk) instead of downloading again.
  • Recommend — detect RAM and GPU VRAM, then pick the best model for this machine from a curated, remotely updated catalog — per use case: chat, code, reasoning, embedding (RAG), speech-to-text, text-to-speech, vision. One call provisions it (companion files like TTS decoders and vision projectors included), and users are told when a better model appears.
  • Update — check Hugging Face Hub for newer revisions without downloading a byte; pull updates with resumable downloads.
  • Deduplicate — replace redundant copies with hardlinks to reclaim disk space. Dry-run by default, journaled, undoable.

Desktop only (Windows / macOS / Linux). Mobile is out of scope.

Quick start (CLI)

$ modelshelf scan
$ modelshelf list --duplicates
$ modelshelf recommend        # which curated chat models fit this machine's RAM/GPU?
$ modelshelf recommend --task stt   # …or code, reasoning, embedding, tts, vision
$ modelshelf recommend --pull # download the best one (asks before removing an old pick)
$ modelshelf pull hf:bartowski/Meta-Llama-3.1-8B-Instruct-GGUF --quant Q4_K_M
$ modelshelf update           # newer revisions? a better model for this machine?
$ modelshelf catalog update   # refresh the recommendation catalog now
$ modelshelf dedup            # dry run: shows reclaimable space
$ modelshelf dedup --apply

Quick start (SDK)

use modelshelf::{Shelf, OpenOptions, ScanOptions, ModelSpec, PullOptions};

let shelf = Shelf::open(OpenOptions::default().app_id("com.example.myapp"))?;
shelf.scan(&ScanOptions::default())?;

// Reuse an existing local copy if one exists anywhere; download otherwise.
let spec = ModelSpec::parse("hf:bartowski/Meta-Llama-3.1-8B-Instruct-GGUF/Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf")?;
let entry = shelf.ensure(&spec, &PullOptions::default())?;

// Feed this path to llama.cpp or any GGUF-capable runtime.
let path = shelf.blob_path(&entry.id)?;

Or let modelshelf pick a model for the machine it is running on — the local-first "just works" setup for a desktop app's first run:

use modelshelf::{Hardware, RecommendOptions, PullOptions};

// Detects RAM / NVIDIA VRAM / Apple Silicon; picks from the curated catalog;
// downloads only when no local copy can be reused.
let hw = Hardware::detect();
let outcome = shelf.provision_recommended(&hw, &RecommendOptions::default(), &PullOptions::default())?;
let path = shelf.blob_path(&outcome.entry.id)?;

// Later runs: tell the user when the catalog has something better.
if let Some(upgrade) = shelf.recommend(&hw)?.upgrade {
    println!("A better model is available: {}", upgrade.to.name);
    // On consent: provision_recommended() again, then optionally
    // shelf.remove_if_unreferenced(&upgrade.from) — it refuses to touch
    // anything still referenced or living outside the shared store.
}

The catalog is versioned data in crates/modelshelf/catalog/, refreshed from this repository at most weekly with an embedded fallback — see its README for the curation policy. A runnable version of the above lives at cargo run -p modelshelf --example recommend.

modelshelf manages models; it does not run them. Inference stays in your app's runtime of choice.

How it works

~/.modelshelf/
├── registry.json    # which models exist, where, which apps use them
├── registry.lock    # advisory lock for safe concurrent access
├── blobs/           # canonical content-addressed store (sha256-<hex>)
├── downloads/       # resumable download state
├── journal/         # dedup operation records (undo)
└── catalog.json     # cached recommendation catalog (client-side)

The convention is specified in docs/SPEC.md so that other tools and languages can implement it independently. Safety rules — including which ecosystem directories modelshelf will never write into — are part of the spec.

AI-assisted development

  • Integrating modelshelf into your app with Claude Code / Codex / Cursor? Point your assistant at docs/AI-INTEGRATION.md — a self-contained guide with the JSON CLI, exit codes, SDK recipes, and a ready-made prompt.
  • Contributing to modelshelf itself with an agent? Agents read AGENTS.md (Claude Code picks it up via CLAUDE.md): build gates, hard safety rules, and the catalog update runbook.

Status

Early development, pre-0.1. The on-disk format may still change until 0.1.0 is tagged.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md. All commits, issues, and pull requests are in English.

If modelshelf is useful to you, you can support its development via GitHub Sponsors.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

A shared local LLM model registry for desktop apps: discover, deduplicate, download, and update models across apps. Rust SDK + CLI, no daemon.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages