# How llmfit's Tauri Desktop Application Integrates with the Core Rust Library

> Discover how llmfit's Tauri desktop app integrates with the core Rust library. The app exposes llmfit-core functionality via serializable commands for seamless JavaScript to Rust interaction.

- Repository: [Alex Jones/llmfit](https://github.com/AlexsJones/llmfit)
- Tags: internals
- Published: 2026-08-22

---

**The llmfit desktop application acts as a thin Tauri wrapper that exposes `llmfit-core` functionality through serializable commands, allowing the JavaScript UI to invoke Rust methods for hardware detection, model fitting, and provider operations.**

The AlexsJones/llmfit repository implements a machine learning model fitting tool with a native desktop interface. The llmfit Tauri desktop application integrates with the core Rust library through a command-based architecture that keeps hardware detection, model catalog management, and inference provider logic decoupled from the presentation layer.

## Command Registration in the Tauri Layer

The integration begins in [`llmfit-desktop/src/main.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-desktop/src/main.rs), where the Tauri builder registers Rust functions as invokable commands. These commands serve as the exclusive bridge between the web-based UI and the system-level capabilities of the core library.

### Exposing Core APIs as Tauri Commands

The desktop application defines several `#[tauri::command]` functions that act as thin shims over `llmfit-core` APIs:

- **`get_system_specs`** – Wraps `SystemSpecs::detect()` from [`llmfit-core/src/hardware.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/hardware.rs) to gather RAM, CPU, and GPU information
- **`get_model_fits`** – Invokes `ModelDatabase::new()` from [`llmfit-core/src/models.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/models.rs) and `ModelFit::analyze()` from [`llmfit-core/src/fit.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/fit.rs) to evaluate models against detected hardware
- **`start_pull`** and **`poll_pull`** – Interface with the `OllamaProvider` for model download management
- **`is_ollama_available`** – Checks provider connectivity

Each command returns results serialized through `#[derive(Serialize)]` structs, enabling automatic marshalling to the JavaScript side without custom deserialization logic.

```rust
#[tauri::command]
fn get_system_specs() -> Result<SystemInfo, String> {
    let specs = SystemSpecs::detect();
    let gpus = specs.gpus.iter().map(|g| GpuInfoJs {
        name: g.name.clone(),
        vram_gb: g.vram_gb,
        backend: format!("{:?}", g.backend),
        count: g.count,
        unified_memory: g.unified_memory,
    }).collect();

    Ok(SystemInfo {
        total_ram_gb: specs.total_ram_gb,
        available_ram_gb: specs.available_ram_gb,
        cpu_name: specs.cpu_name.clone(),
        cpu_cores: specs.total_cpu_cores,
        gpus,
        unified_memory: specs.unified_memory,
    })
}

```

## Managing Shared State Across the Rust-JavaScript Boundary

The integration uses Tauri's state management to maintain persistent connections across command invocations. An `AppState` struct holds a singleton `OllamaProvider` (from [`llmfit-core/src/providers.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/providers.rs)) and a mutex-protected optional pull handle.

This state is injected into commands via `State<'_, AppState>`, allowing the UI to start long-running model downloads and monitor progress without recreating provider objects or losing connection context between calls.

## The JavaScript Bridge and UI Integration

The front-end code in [`llmfit-desktop/ui/app.js`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-desktop/ui/app.js) consumes the registered commands through Tauri's `invoke` API. The JavaScript layer remains purely presentational, delegating all computational work to the Rust core.

```javascript
import { invoke } from '@tauri-apps/api/tauri';

async function loadSystemInfo() {
  const info = await invoke('get_system_specs');
  document.getElementById('cpu').textContent = `${info.cpu_name} (${info.cpu_cores} cores)`;
  document.getElementById('ram').textContent = `${info.total_ram_gb.toFixed(1)} GB total`;
  // Render GPU list …
}
loadSystemInfo();

```

When fetching model fits, the JavaScript invokes the Rust command and renders the returned JSON array:

```javascript
async function loadModelFits() {
  const fits = await invoke('get_model_fits');
  const tbody = document.querySelector('#model-table tbody');
  tbody.innerHTML = '';
  fits.forEach(fit => {
    const row = document.createElement('tr');
    row.innerHTML = `
      <td>${fit.name}</td>
      <td>${fit.params_b.toFixed(2)} B</td>
      <td>${fit.quant}</td>
      <td>${fit.fit_level}</td>
      <td>${fit.run_mode}</td>
      <td>${fit.score.toFixed(2)}</td>
    `;
    tbody.appendChild(row);
  });
}
loadModelFits();

```

The corresponding Rust command handles the complex analysis logic:

```rust
#[tauri::command]
fn get_model_fits() -> Result<Vec<ModelFitInfo>, String> {
    let specs = SystemSpecs::detect();
    let db = ModelDatabase::new();

    let fits = db.get_all_models()
        .iter()
        .map(|m| ModelFit::analyze(m, &specs))
        .collect::<Vec<_>>();

    let ranked = llmfit_core::fit::rank_models_by_fit(fits);

    Ok(ranked.into_iter().map(|f| ModelFitInfo {
        name: f.model.name.clone(),
        params_b: f.model.parameters_raw.unwrap_or(0) as f64 / 1e9,
        quant: f.best_quant.clone(),
        fit_level: match f.fit_level {
            FitLevel::Perfect => "Perfect",
            FitLevel::Good => "Good",
            FitLevel::Marginal => "Marginal",
            FitLevel::TooTight => "Too Tight",
        }.to_string(),
        run_mode: match f.run_mode {
            RunMode::Gpu => "GPU",
            RunMode::CpuOffload => "CPU Offload",
            RunMode::CpuOnly => "CPU Only",
            RunMode::MoeOffload => "MoE Offload",
            RunMode::TensorParallel => "Tensor Parallel",
        }.to_string(),
        score: f.score,
        memory_required_gb: f.memory_required_gb,
        memory_available_gb: f.memory_available_gb,
        utilization_pct: f.utilization_pct,
        estimated_tps: f.estimated_tps,
        use_case: format!("{:?}", f.use_case),
        runtime: match f.runtime {
            InferenceRuntime::LlamaCpp => "llama.cpp",
            InferenceRuntime::Mlx => "MLX",
            InferenceRuntime::Vllm => "vLLM",
            InferenceRuntime::Unsupported => "unsupported",
        }.to_string(),
        installed: f.installed,
        notes: f.notes.clone(),
        release_date: f.model.release_date.clone(),
    }).collect())
}

```

## Architectural Separation of Concerns

The integration maintains strict boundaries between the Tauri-specific desktop code and the reusable core library. All domain logic—including hardware detection in [`llmfit-core/src/hardware.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/hardware.rs), model catalog management in [`llmfit-core/src/models.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/models.rs), and fit analysis in [`llmfit-core/src/fit.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/fit.rs)—resides in `llmfit-core`.

The desktop crate handles only three responsibilities: registering Tauri commands, managing stateful provider connections, and packaging static assets ([`index.html`](https://github.com/AlexsJones/llmfit/blob/main/index.html), [`styles.css`](https://github.com/AlexsJones/llmfit/blob/main/styles.css)). This separation ensures the core library remains usable by the CLI (`llmfit-tui`) and Python wrapper without pulling in Tauri dependencies or web runtime overhead.

## Summary

- **Command-based integration** – [`llmfit-desktop/src/main.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-desktop/src/main.rs) registers thin command wrappers that delegate to `llmfit-core` APIs like `SystemSpecs::detect()` and `ModelFit::analyze()`
- **Automatic serialization** – Rust structs derive `Serialize` to enable seamless data transfer across the JavaScript boundary without custom marshalling code
- **Stateful persistence** – The `AppState` struct maintains `OllamaProvider` instances across invocations using Tauri's dependency injection system
- **Clean architecture** – All heavy logic stays in `llmfit-core`, making the library reusable by CLI and Python consumers while the desktop layer focuses strictly on UI concerns

## Frequently Asked Questions

### How does the Tauri front-end call functions in the Rust core library?

The front-end uses Tauri's `invoke` API to call commands registered in [`llmfit-desktop/src/main.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-desktop/src/main.rs). Each command is a Rust function marked with `#[tauri::command]` that internally calls methods from `llmfit-core`, such as `SystemSpecs::detect()` or `ModelDatabase::new()`, and returns serializable structs that Tauri automatically converts to JavaScript objects.

### Where is the application state stored in the llmfit desktop app?

Application state is stored in an `AppState` struct managed by Tauri's state system. This struct, defined in the desktop crate, holds a singleton `OllamaProvider` from [`llmfit-core/src/providers.rs`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-core/src/providers.rs) and a mutex-protected pull handle. Tauri injects this state into command handlers via `State<'_, AppState>`, allowing persistent connections across multiple UI interactions.

### Can the core library be used without the Tauri desktop interface?

Yes. The `llmfit-core` crate contains no Tauri dependencies and exposes a standard Rust API used by both the desktop application and the `llmfit-tui` CLI tool. Hardware detection, model fitting, and provider interactions all live in `llmfit-core`, making the library consumable by any Rust binary, Python wrapper, or alternative interface without the overhead of the webview runtime.