hf-hub Rust Library

repository·main·Indexed 43 days ago

https://github.com/huggingface/hf-hub

A Rust library for interacting with the Hugging Face Hub. It provides an asynchronous HFClient (default) and a synchronous HFClientSync (via the blocking feature) for managing models, datasets, spaces, and buckets. The library supports file uploads, downloads, and streaming pagination. It also includes Wasm support via hf-hub-wasm-smoke for streaming downloads directly in the browser, and the hfrs command-line tool.

Tokens
35.7K
Snippets
106
Records
172
Agent score
55%

What's inside hf-hub

  1. WebAssembly (Wasm) support and limitations

    main

    The hf-hub crate compiles for wasm32-unknown-unknown.

    Supported Features:

    • Pure-HTTP modules (repository, spaces, users) are available.
    • HTTP-only methods on HFRepository (e.g., exists, info, list_*) are available.
    • Streaming downloads via download_file_stream() are supported.

    Unsupported Features (Gated behind #[cfg(not(target_family = "wasm"))]):

    • Methods that touch the filesystem (download_file, snapshot_download, upload_file).
    • The cache and buckets modules.
    • The blocking wrappers.

    Implementation Notes:

    • When modifying client, error, retry, xet, or download.rs, ensure the Wasm build remains green.
    • reqwest's Wasm backend has a reduced API (no is_connect, no redirect() policy) and its streams are !Send.
  2. Build the hf-hub-wasm-smoke WASM package

    main

    To build the WASM package, use the provided build script. This requires a nightly Rust toolchain and wasm-bindgen-cli version 0.2.121. The build script will attempt to install the correct version of wasm-bindgen-cli if it is missing.

    Important: The build uses specific threaded-wasm flags (+atomics,+bulk-memory,+mutable-globals, shared memory, and getrandom_backend=wasm_js) because the hf-xet dependency requires them for internal threaded WASM operations.

    Outputs are generated in the pkg/ directory:

    • hf_hub_wasm_smoke.js
    • hf_hub_wasm_smoke.d.ts
    • hf_hub_wasm_smoke_bg.wasm
    ./build_wasm.sh
  3. Install hf-hub via Cargo

    main

    Add hf-hub to your Cargo.toml dependencies. By default, it provides an async interface using HFClient. If you require a synchronous interface, you must enable the blocking feature.

    Async (Default):

    [dependencies]
    hf-hub = "1.0.0"

    Synchronous (Blocking):

    [dependencies]
    hf-hub = { version = "1.0.0", features = ["blocking"] }
  4. Install the hfrs CLI

    main

    The hfrs command-line tool provides a terminal interface to the Hugging Face Hub. Install it using cargo install from the GitHub repository:

    cargo install --git https://github.com/huggingface/hf-hub.git hfrs

    After installation, run hfrs --help to view available commands.

  5. Use the synchronous HFClientSync interface

    main

    If you enable the blocking feature, you can use HFClientSync. This client manages its own internal tokio runtime, so you do not need to provide one.

    use hf_hub::HFClientSync;
    
    fn main() -> hf_hub::HFResult<()> {
        let client = HFClientSync::new()?;
    
        let Ok(info) = client.model("openai-community", "gpt2").info().send() else {
            unreachable!("handle type guarantees the Model variant");
        };
        println!("Model: {} (downloads: {:?})", info.id, info.downloads);
        Ok(())
    }
    use hf_hub::HFClientSync;
    
    fn main() -> hf_hub::HFResult<()> {
        let client = HFClientSync::new()?;
    
        let Ok(info) = client.model("openai-community", "gpt2").info().send() else {
            unreachable!("handle type guarantees the Model variant");
        };
        println!("Model: {} (downloads: {:?})", info.id, info.downloads);
        Ok(())
    }
  6. Serve WASM with COOP/COEP headers

    main

    Because the hf-xet dependency uses threaded WASM, the browser requires SharedArrayBuffer support. To enable this, you must serve your application using a server that sets the following HTTP headers:

    • Cross-Origin-Opener-Policy: same-origin
    • Cross-Origin-Embedder-Policy: require-corp

    You can use sfz to quickly serve with these headers using the --coi flag:

    sfz --coi -p 8080
  7. Use the async HFClient interface

    main

    The HFClient provides an asynchronous interface for interacting with the Hub. It is the default client and requires an async runtime like tokio.

    use hf_hub::HFClient;
    
    #[tokio::main]
    async fn main() -> hf_hub::HFResult<()> {
        let client = HFClient::new()?;
    
        // Get model info
        let Ok(info) = client.model("openai-community", "gpt2").info().send().await else {
            unreachable!("handle type guarantees the Model variant");
        };
    
        println!("Model: {} (downloads: {:?})", info.id, info.downloads);
    
        Ok(())
    }
    use hf_hub::HFClient;
    
    #[tokio::main]
    async fn main() -> hf_hub::HFResult<()> {
        let client = HFClient::new()?;
    
        // Get model info
        let Ok(info) = client.model("openai-community", "gpt2").info().send().await else {
            unreachable!("handle type guarantees the Model variant");
        };
    
        println!("Model: {} (downloads: {:?})", info.id, info.downloads);
    
        Ok(())
    }
  8. Parse Hugging Face raw diffs into HFFileDiff

    main

    The Hugging Face Hub provides a raw diff format (a line-oriented variant of git's raw diff) to inspect file changes between repository revisions. You can use HFFileDiff to access metadata for each changed file, such as blob IDs, file status, and size.

    For rename or copy operations, file_path represents the source path, while new_file_path contains the destination path. For additions, deletions, or in-place modifications, new_file_path will be None.

    // Example of the data structure returned by the parser
    HFFileDiff {
        old_blob_id: String, // All zeroes for additions
        new_blob_id: String, // All zeroes for deletions
        status: GitStatus,   // e.g., Addition, Deletion, Modification, Rename, Copy
        file_path: String,   // Source path
        new_file_path: Option<String>, // Destination path for Rename/Copy
        is_binary: bool,     // True if marked 'B', false if 'T'
        new_file_size: u64,  // Size in bytes
    }
  9. Use RepoTypeAny for runtime repository type dispatch

    main

    When the repository kind is decided at runtime (for example, via a CLI flag or a configuration string), use the RepoTypeAny enum.

    RepoTypeAny implements FromStr, allowing you to parse strings like "model", "models", "dataset", "datasets", etc., into a valid type. It also implements the RepoType trait, so it can be used as a type parameter for HFRepository<RepoTypeAny>.

    Note: While RepoTypeAny supports all standard file, commit, and repository management APIs (uploads, downloads, tree listings, etc.), it does not support Space-only methods like runtime(), pause(), restart(), secrets(), variables(), or hardware(). Those require a typed HFRepository<RepoTypeSpace>.

    use hf_hub::{HFClient, RepoTypeAny};
    
    let kind: RepoTypeAny = "datasets".parse().unwrap();
    let repo: HFRepository<RepoTypeAny> = client.repository(kind, "rajpurkar", "squad");
  10. How repository types are handled in hf-hub

    main

    The hf-hub crate uses a type-level marker system to encode the Hugging Face Hub repository kind (model, dataset, space, or kernel) into the type system. This allows methods that behave differently depending on the repository type (like info()) to dispatch at compile time.

    There are two ways to work with repository types:

    1. Typed Markers: Use specific marker structs like RepoTypeModel, RepoTypeDataset, etc. This is preferred when the repository kind is known at compile time. It provides access to type-specific methods (e.g., RepoTypeSpace allows access to runtime() and pause() on an HFRepository).
    2. Runtime Dispatch (RepoTypeAny): Use the RepoTypeAny enum when the repository kind is determined at runtime (e.g., from a CLI flag or configuration string). This allows you to use a single HFRepository<RepoTypeAny> type, but you lose access to type-specific methods like those for Spaces.

    All markers and the RepoTypeAny enum implement the RepoType trait, which provides the singular name, plural name, and URL prefix for the repository kind.

    use hf_hub::{HFClient, RepoTypeDataset};
    # #[tokio::main] async fn main() -> hf_hub::HFResult<()> {
    // Typed shortcut — preferred when the kind is known at the call site:
    let repo = client.dataset("rajpurkar", "squad");
    
    // Explicit marker — useful when the kind comes from a generic context:
    let repo = client.repository(RepoTypeDataset, "rajpurkar", "squad");
    # let _ = repo; Ok(()) }
  11. How repository handles work

    main

    Instead of passing repository identifiers to every method, you can bind them to a typed handle using HFClient methods. The repository kind (Model, Dataset, Space, or Kernel) is encoded in the type system using marker structs. This allows for type-specific methods (e.g., Space-specific methods like runtime or pause are only available on HFRepository<RepoTypeSpace>).

    Common constructors:

    • client.model(owner, name)
    • client.dataset(owner, name)
    • client.space(owner, name)
    • client.kernel(owner, name)

    You can use split_id to convert a single string like "owner/name" into the (owner, name) pair required by these constructors.

    use hf_hub::{HFClient, split_id};
    
    # #[tokio::main] async fn main() -> hf_hub::HFResult<()> {
    let client = HFClient::new()?;
    let (owner, name) = split_id("openai-community/gpt2");
    let model = client.model(owner, name);
    # let _ = model; Ok(()) }
  12. How to manage Hugging Face Spaces

    main

    Space-specific operations (runtime, hardware, secrets, variables, pause/restart, duplicate) are performed using methods on an HFRepository<RepoTypeSpace> instance. To get a handle for a Space, use HFClient::space(owner, name). There is no separate HFSpace wrapper; you call the methods directly on the repository handle returned by the client.

    let client = HFClient::builder().build()?;
    let space = client.space("owner", "name");
    // Call methods directly on the space handle
    let runtime = space.runtime().send().await?;