ModelDownloader in Palmier Pro: How It Manages AI Model Caching

TLDR: ModelDownloader is a Swift utility in the palmier-io/palmier-pro repository that downloads, verifies, compiles, and installs machine‑learning encoders into a dedicated Application Support cache, ensuring AI models are fetched only once and reused securely across app launches.

The ModelDownloader class, implemented in Sources/PalmierPro/Search/Models/ModelDownloader.swift, orchestrates the complete lifecycle of AI model assets required for Palmier Pro’s visual search features. It stores assets in ~/Library/Application Support/PalmierPro/Models using a versioned directory structure that prevents duplicate downloads and guarantees file integrity through cryptographic hashing.

How ModelDownloader Manages AI Model Caching

Determining the Install Location

The downloader first resolves the permanent cache directory using Apple’s standard filesystem APIs. It calls FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask) and appends the path component PalmierPro/Models to establish the root of the model cache. This ensures compliance with macOS conventions and proper sandboxing behavior.

Checking Existing Installs (Idempotency)

Before initiating any network requests, the installed(for:) method checks the cache for existing assets. It looks for three required items: ImageEncoder.mlmodelc, TextEncoder.mlmodelc, and tokenizer/tokenizer.json. If all files exist at the expected version path, the method returns an InstalledModel struct immediately, skipping the download process entirely. This idempotent design prevents redundant network traffic and speeds up app launches when models are already present.

Downloading and Streaming Files

When assets are missing, the downloader creates a temporary staging area named palmier-model-<UUID> under the system temporary directory. The download(_:to:progress:) method then streams each file described in the manifest using URLSession with a custom URLSessionDownloadDelegate that reports fractional progress. This allows the caller to aggregate overall progress across the image encoder, text encoder, and tokenizer archives.

Verifying Integrity with SHA-256

After each download completes, the verify(_:sha256:) method computes the SHA-256 hash of the file and compares it against the expected checksum defined in the model manifest. If the hash does not match, the installation aborts immediately, preventing corrupted or tampered models from entering the cache.

Compiling and Moving to Permanent Cache

The unzip(_:in:) method extracts archives using /usr/bin/ditto. If the extracted entry is an .mlpackage, it is compiled into a .mlmodelc bundle using MLModel.compileModel(at:). Tokenizer archives remain as plain files. Finally, the compiled assets are moved from the staging area into a versioned directory (<model>-v<version>) within the Application Support cache, accompanied by a spec.json metadata file. The temporary staging directory is automatically removed after completion.

Caching Strategy and Security

The ModelDownloader implements a filesystem‑based cache with several safeguards:

  • Versioned Directories: Each model resides in a directory named <model>-v<version>, allowing simultaneous coexistence of different versions without conflicts.
  • Idempotent Install: The installed(for:) check runs before every download attempt, ensuring network activity only occurs when necessary.
  • Integrity Guard: SHA-256 verification guarantees that cached files match the exact bytes specified in the manifest, protecting against corruption or supply‑chain attacks.
  • Atomic Installation: By staging files in a temporary location and moving them only after successful verification and compilation, the system never leaves partial or broken models in the permanent cache.

Complete Implementation Example

import PalmierPro

// 1️⃣ Build the manifest (normally fetched from a server)
let manifest = ModelDownloader.Manifest(
    model: "siglip2",
    version: 1,
    embeddingDim: 768,
    imageSize: 224,
    contextLength: 77,
    files: .init(
        imageEncoder: .init(name: "image-encoder.zip", sha256: "a1b2...", bytes: 12_345_678),
        textEncoder:   .init(name: "text-encoder.zip",   sha256: "c3d4...", bytes: 9_876_543),
        tokenizer:     .init(name: "tokenizer.zip",     sha256: "e5f6...", bytes: 123_456)
    )
)

// 2️⃣ Base URL where the zip files are hosted
let baseURL = URL(string: "https://models.palmier.io/siglip2/")!

// 3️⃣ Create the downloader and start the install
Task {
    do {
        let downloader = ModelDownloader()
        let installed = try await downloader.install(
            manifest: manifest,
            baseURL: baseURL,
            progress: { fraction in
                print("Overall progress: \(Int(fraction * 100))%")
            })
        // 4️⃣ Use the installed model
        let imageEncoder = try MLModel(contentsOf: installed.imageEncoderURL)
        let textEncoder  = try MLModel(contentsOf: installed.textEncoderURL)
        // … feed the models into VisualEmbedder …
    } catch {
        print("Installation failed:", error)
    }
}

This example constructs a Manifest, points the downloader at a remote server, and calls install(manifest:baseURL:progress:). The method automatically checks the cache, downloads missing pieces, verifies checksums, unpacks archives, compiles Core ML bundles, and returns URLs to ready‑to‑use assets.

Key Source Files

The following files define the model caching architecture:

Summary

  • ModelDownloader handles the complete AI model lifecycle in Palmier Pro, from network download to local compilation.
  • It caches models in ~/Library/Application Support/PalmierPro/Models using versioned directories to support multiple simultaneous versions.
  • Idempotent checks via installed(for:) prevent redundant downloads by verifying the existence of ImageEncoder.mlmodelc, TextEncoder.mlmodelc, and tokenizer files before network activity.
  • SHA-256 verification ensures cache integrity, while atomic staging guarantees that only fully verified and compiled models reach the permanent cache.
  • The utility compiles .mlpackage files into .mlmodelc bundles using MLModel.compileModel(at:) for optimal runtime performance.

Frequently Asked Questions

Where does ModelDownloader store cached AI models?

Models are stored in the Application Support directory at ~/Library/Application Support/PalmierPro/Models/<model>-v<version>. This location persists across app launches and follows macOS conventions for user-specific application data.

How does ModelDownloader handle different versions of the same model?

The downloader embeds the version number in the directory name (e.g., siglip2-v1). When checking for existing installs, installed(for:) looks for the specific version requested in the manifest, allowing multiple versions to coexist without collision and enabling rollback scenarios.

What happens if a downloaded file fails the integrity check?

The verify(_:sha256:) method computes the SHA-256 hash of the downloaded file and compares it to the manifest. If the hash mismatches, the method throws an error and aborts the installation, preventing corrupted or malicious files from entering the cache. The temporary staging files are cleaned up automatically.

How does ModelDownloader integrate with Palmier Pro’s search features?

After successful installation, ModelDownloader returns an InstalledModel struct containing URLs to the compiled encoder bundles. This struct is consumed by VisualEmbedder (defined in Sources/PalmierPro/Search/VisualEmbedder.swift), which loads the models using MLModel(contentsOf:) to generate embeddings for visual search queries.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →