Skip to content

26. Development And Contribution

FerrisMind edited this page Sep 10, 2025 · 1 revision

Development and Contribution

Update Summary

Changes Made

  • Added new section on Auto Device Selection with CUDA → Metal → CPU runtime detection
  • Updated Hybrid Architecture Overview to include Metal backend support
  • Added Plugin Extensibility Architecture section based on new proposal
  • Updated Cargo.toml section sources to reflect Metal feature addition
  • Enhanced diagram to include Metal backend
  • Updated section sources to include new device selection files

Table of Contents

  1. Hybrid Architecture Overview
  2. Build Process and Tooling
  3. Development Environment Setup
  4. Contribution Workflow
  5. Adding New Model Architectures
  6. Testing Strategy
  7. Versioning and Release Process
  8. Coding Standards
  9. CodeMirror Integration
  10. Code Quality and Pre-commit Hooks
  11. Auto Device Selection
  12. Plugin Extensibility Architecture

Hybrid Architecture Overview

The Oxide Lab application employs a hybrid desktop architecture combining a Svelte frontend with a Rust backend, unified through the Tauri framework. This design enables high-performance machine learning inference via Rust while maintaining a responsive and modern user interface with Svelte.

The architecture follows a clear separation of concerns:

  • Frontend (Svelte): Handles UI rendering, user interactions, and state management
  • Backend (Rust): Manages model loading, inference execution, and system-level operations
  • Integration Layer (Tauri): Facilitates secure communication between frontend and backend via command invocation

``mermaid graph TD A[Svelte Frontend] --> |Tauri Commands| B(Rust Backend) B --> C[Candle ML Framework] B --> D[GGUF Model Files] A --> E[Browser Rendering Engine] B --> F[System Resources
CPU/GPU/Metal] style A fill:#4f46e5,stroke:#3730a3,color:white style B fill:#059669,stroke:#047857,color:white style C fill:#7c3aed,stroke:#6d28d9,color:white style D fill:#dc2626,stroke:#b91c1c,color:white style E fill:#0891b2,stroke:#0e7490,color:white style F fill:#ea580c,stroke:#c2410c,color:white


**Diagram sources**
- [src-tauri/Cargo.toml](file://src-tauri/Cargo.toml#L1-L54)
- [vite.config.js](file://vite.config.js#L1-L33)

**Section sources**
- [src-tauri/Cargo.toml](file://src-tauri/Cargo.toml#L1-L54)
- [vite.config.js](file://vite.config.js#L1-L33)

### Frontend-Backend Communication

Communication between the Svelte frontend and Rust backend occurs through Tauri's command system. The backend exposes specific functions as Tauri commands, which the frontend can invoke asynchronously.

``mermaid
sequenceDiagram
participant Frontend as Svelte Frontend
participant Tauri as Tauri Bridge
participant Backend as Rust Backend
Frontend->>Tauri : invoke("load_model", {path, device})
Tauri->>Backend : Dispatch load_model command
Backend->>Backend : Load GGUF model from disk
Backend->>Backend : Initialize model on specified device
Backend-->>Tauri : Return success/failure
Tauri-->>Frontend : Resolve/Reject promise
Frontend->>Tauri : invoke("generate_stream", {prompt})
Tauri->>Backend : Dispatch generate_stream command
Backend->>Backend : Process token generation
Backend->>Tauri : emit("token", {content})
Tauri->>Frontend : Receive "token" event
Backend->>Tauri : emit("done", {stats})
Tauri->>Frontend : Receive "done" event

Diagram sources

  • src-tauri/src/lib.rs
  • src-tauri/src/api/mod.rs

Build Process and Tooling

The build process integrates multiple tools to create a cohesive desktop application from separate frontend and backend components.

Build Toolchain Overview

``mermaid flowchart LR A[Source Code] --> B{Development Mode} A --> C{Production Build} B --> D[Vite Development Server] D --> E[Tauri Dev Bridge] E --> F[Rust Cargo Build] F --> G[Hot Reload] C --> H[Vite Build] H --> I[Frontend Bundle] I --> J[Tauri Bundle] J --> K[Rust Cargo Build] K --> L[Final Binary] style B fill:#f97316,stroke:#ea580c,color:white style C fill:#059669,stroke:#047857,color:white style D fill:#3b82f6,stroke:#2563eb,color:white style F fill:#059669,stroke:#047857,color:white style H fill:#3b82f6,stroke:#2563eb,color:white style J fill:#14b8a6,stroke:#0d9488,color:white style K fill:#059669,stroke:#047857,color:white


**Diagram sources**
- [vite.config.js](file://vite.config.js#L1-L33)
- [src-tauri/Cargo.toml](file://src-tauri/Cargo.toml#L1-L54)
- [package.json](file://package.json#L1-L52)

### Makefile Integration

The project uses a Makefile to streamline common development tasks, providing simple commands for complex operations:

makefile dev: cargo tauri dev

build: cargo tauri build

lint: npx svelte-check


This abstraction simplifies the development workflow and ensures consistency across different developer environments.

**Section sources**
- [Makefile](file://Makefile)
- [package.json](file://package.json#L1-L52)
- [vite.config.js](file://vite.config.js#L1-L33)

## Development Environment Setup

### Prerequisites

Before starting development, ensure the following tools are installed:
- **Rust**: Latest stable version with Cargo
- **Node.js**: Version 18 or higher
- **Tauri CLI**: `cargo install tauri-cli`
- **Python**: Required for some Candle dependencies

### Initial Setup

1. Clone the repository
2. Install Node.js dependencies: `npm install`
3. Ensure Rust toolchain is installed: `rustup update`
4. Verify Tauri setup: `cargo tauri info`

The Tauri configuration in `tauri.conf.json` specifies the development server settings, including the port (1420) and hot module replacement configuration.

**Section sources**
- [package.json](file://package.json#L1-L52)
- [vite.config.js](file://vite.config.js#L1-L33)
- [src-tauri/tauri.conf.json](file://src-tauri/tauri.conf.json#L1-L52)

## Contribution Workflow

### Running in Development Mode

To run the application in debug mode with hot reloading:

bash cargo tauri dev


This command:
1. Starts the Vite development server for the Svelte frontend
2. Compiles the Rust backend with debug symbols
3. Launches the Tauri application window
4. Establishes a WebSocket connection for hot reloading

The Vite configuration in `vite.config.js` sets up the development server to work seamlessly with Tauri, using port 1420 and ignoring changes in the `src-tauri` directory to prevent unnecessary rebuilds.

### Debugging Strategies

- **Frontend**: Use browser developer tools via Tauri's debug mode
- **Backend**: Utilize Rust logging with `println!` or proper logging crates
- **Integration**: Monitor Tauri command invocations and responses

**Section sources**
- [vite.config.js](file://vite.config.js#L1-L33)
- [src-tauri/tauri.conf.json](file://src-tauri/tauri.conf.json#L1-L52)
- [package.json](file://package.json#L1-L52)

## Adding New Model Architectures

### ModelBackend Trait

New model architectures are integrated by implementing the `ModelBackend` trait. This trait defines the interface between the application core and specific model implementations.

rust pub trait ModelBackend: Send { fn forward_layered(&mut self, input: &Tensor, position: usize) -> Result<Tensor, String>; }


The trait requires implementation of the `forward_layered` method, which processes input tensors and returns output tensors for token generation.

``mermaid
classDiagram
class ModelBackend {
<<trait>>
+forward_layered(input : &Tensor, position : usize) Result<Tensor, String>
}
class AnyModel {
-inner : Box<dyn ModelBackend + Send>
+from_qwen3(m : ModelWeights) AnyModel
+from_candle_qwen3(m : ModelForCausalLM) AnyModel
}
class ModelWeights {
-inner : candle_transformers : : models : : quantized_qwen3 : : ModelWeights
+from_gguf(content : Content, reader : &mut R, device : &Device) Result<Self, String>
}
ModelBackend <|-- ModelWeights
ModelBackend <|-- AnyModel
AnyModel o-- ModelWeights

Diagram sources

  • src-tauri/src/models/common/model.rs
  • src-tauri/src/models/qwen3.rs

Section sources

  • src-tauri/src/models/common/model.rs
  • src-tauri/src/models/qwen3.rs

Implementation Steps

To add a new model architecture:

  1. Create a new module in src-tauri/src/models/ (e.g., my_model.rs)
  2. Define a struct that will hold the model weights and configuration
  3. Implement the ModelBackend trait for your struct
  4. Update the model registry to include your model type
  5. Add any necessary dependencies to Cargo.toml

The AnyModel wrapper provides a unified interface for different model types, allowing the core application logic to remain agnostic to specific model architectures.

Testing Strategy

Backend Testing

Rust components should be tested using Rust's built-in testing framework. Tests should cover:

  • Model loading and initialization
  • Token generation accuracy
  • Error handling for invalid inputs
  • Device switching functionality

Example test structure:

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_model_load() {
        // Test model loading from GGUF file
    }
    
    #[test]
    fn test_token_generation() {
        // Test forward pass and token output
    }
}

Frontend Testing

Svelte components should be tested using the project's testing framework (implied by svelte-check in package.json). Tests should verify:

  • UI component rendering
  • Event handling
  • State management
  • Integration with Tauri commands
  • CodeMirror rendering functionality

The svelte-check tool provides type checking for Svelte components, helping catch errors before runtime.

Section sources

  • package.json
  • src-tauri/Cargo.toml
  • src/lib/chat/codemirror-renderer.ts

Versioning and Release Process

Versioning Strategy

The project follows semantic versioning (SemVer) as indicated by the version field in tauri.conf.json and Cargo.toml. The current version is 0.1.0, indicating an initial development release.

CHANGELOG Management

The CHANGELOG.md file should be updated for each release with:

  • New features
  • Bug fixes
  • Breaking changes
  • Performance improvements

Each entry should include:

  • Version number
  • Release date
  • Categorized changes
  • Contributor credits when appropriate

Release Process

  1. Update version number in tauri.conf.json and Cargo.toml
  2. Update CHANGELOG.md with release notes
  3. Create a git tag for the release version
  4. Run cargo tauri build to create the production binary
  5. Test the built application on target platforms
  6. Publish the release with binaries and release notes

Section sources

  • src-tauri/Cargo.toml
  • src-tauri/tauri.conf.json
  • CHANGELOG.md

Coding Standards

Rust Code Standards

  • Formatting: Follow Rust standard formatting (rustfmt)
  • Documentation: Document public APIs with doc comments
  • Error Handling: Use descriptive error strings
  • Performance: Minimize allocations in hot paths
  • Safety: Prefer safe Rust over unsafe when possible

Example of proper Rust code:

/// Loads a model from a GGUF file on the specified device
pub fn from_gguf<R: Read + Seek>(
    content: Content, 
    reader: &mut R, 
    device: &Device
) -> Result<Self, String> {
    let cw = candle_transformers::models::quantized_qwen3::ModelWeights::from_gguf(content, reader, device)
        .map_err(|e| e.to_string())?;
    Ok(ModelWeights { inner: cw })
}

TypeScript/Svelte Code Standards

  • Type Safety: Use strict TypeScript configuration
  • Component Structure: Follow Svelte best practices
  • State Management: Use stores for shared state
  • Security: Sanitize user content before rendering
  • Accessibility: Ensure UI components are accessible
  • CodeMirror Integration: Use consistent styling and ensure proper cleanup of mounted components
  • Linting: Adhere to ESLint rules configured in .eslintrc.cjs
  • Formatting: Follow Prettier formatting standards
  • CSS Linting: Comply with Stylelint rules for CSS files

The tsconfig.json file enforces strict type checking, and svelte-check is used to validate component types. ESLint, Stylelint, and Prettier are configured to maintain code quality across the codebase.

Section sources

  • src-tauri/Cargo.toml
  • package.json
  • tsconfig.json
  • .eslintrc.cjs
  • .prettierignore

CodeMirror Integration

Overview

The application now uses CodeMirror for enhanced rendering of markdown code blocks, replacing the previous highlight.js implementation. This provides better syntax highlighting, line numbering, and code interaction capabilities.

The integration consists of:

  • CodeMirror.svelte: Svelte component wrapper for CodeMirror 6
  • CodeMirrorRenderer: Class that detects and replaces standard code blocks with CodeMirror instances
  • markdown_block.ts: Integration point that applies CodeMirror rendering to markdown content

Section sources

  • src/lib/components/CodeMirror.svelte
  • src/lib/chat/codemirror-renderer.ts
  • src/lib/chat/stream/markdown_block.ts

Implementation Details

The CodeMirror integration works through a multi-step process:

  1. Markdown Processing: The renderMarkdownToSafeHtml function in markdown.ts first processes markdown content using marked.js
  2. DOM Mutation Observation: The CodeMirrorRenderer class uses a MutationObserver to detect when new code blocks are added to the DOM
  3. Code Block Replacement: Standard <pre><code> blocks are replaced with CodeMirror component instances
  4. Language Detection: The renderer automatically detects code language from class names or content patterns
  5. Feature Enhancement: Each code block includes a copy button and language label

``mermaid sequenceDiagram participant Markdown as Markdown Parser participant Renderer as CodeMirrorRenderer participant DOM as DOM Container participant CodeMirror as CodeMirror Component Markdown->>DOM : Render markdown to HTML with

 blocks
DOM->>Renderer : MutationObserver detects new code blocks
Renderer->>Renderer : Extract code content and language
Renderer->>Renderer : Create CodeMirror container with toolbar
Renderer->>CodeMirror : Mount CodeMirror component with code
CodeMirror->>DOM : Replace 
 with CodeMirror editor

**Diagram sources**
- [src/lib/chat/markdown.ts](file://src/lib/chat/markdown.ts#L1-L74)
- [src/lib/chat/codemirror-renderer.ts](file://src/lib/chat/codemirror-renderer.ts#L1-L328)
- [src/lib/components/CodeMirror.svelte](file://src/lib/components/CodeMirror.svelte#L1-L266)

### Key Features

- **Syntax Highlighting**: Supports JavaScript, Python, HTML, CSS, JSON, SQL, and XML
- **Line Numbers**: Displayed by default for better code reference
- **Code Copying**: Integrated copy button with visual feedback (checkmark icon)
- **Theme Support**: Automatically follows system dark/light mode preferences
- **Line Wrapping**: Enabled to handle long lines of code
- **Language Detection**: Automatic detection from content patterns when language is not specified

### Configuration

The CodeMirror component accepts the following properties:
- `code`: The code content to display
- `language`: The programming language for syntax highlighting
- `readonly`: Whether the editor is read-only (always true for this application)
- `theme`: Theme setting ('light', 'dark', or 'auto')
- `showLineNumbers`: Whether to display line numbers
- `wrap`: Whether to enable line wrapping

These are configured in the `CodeMirrorRenderer` class with default values optimized for the application's use case.

**Section sources**
- [src/lib/components/CodeMirror.svelte](file://src/lib/components/CodeMirror.svelte#L1-L266)
- [src/lib/chat/codemirror-renderer.ts](file://src/lib/chat/codemirror-renderer.ts#L1-L328)
- [package.json](file://package.json#L1-L52)

## Code Quality and Pre-commit Hooks

### Linting and Formatting Configuration

The project has been enhanced with a comprehensive code quality system using ESLint, Stylelint, and Prettier to ensure consistent code style and identify potential issues early in the development process.

The configuration includes:
- **ESLint**: For JavaScript/TypeScript code analysis with `@typescript-eslint/eslint-plugin`
- **Stylelint**: For CSS/SCSS linting with `stylelint-config-standard-scss`
- **Prettier**: For opinionated code formatting
- **Husky**: For Git hooks management
- **lint-staged**: For running linters on staged files only

These tools are configured in `package.json` with appropriate scripts and lint-staged configuration to automatically apply fixes during the development workflow.

**Section sources**
- [package.json](file://package.json#L1-L52)
- [.eslintrc.cjs](file://.eslintrc.cjs)
- [.prettierignore](file://.prettierignore)
- [.eslintignore](file://.eslintignore)

### Pre-commit Hook Setup

The project uses Husky to manage Git hooks, specifically a pre-commit hook that automatically runs linting and formatting on staged files before commits are finalized.

The pre-commit hook is configured to:
1. Run ESLint with `--fix` on all staged TypeScript, JavaScript, and Svelte files
2. Run Stylelint with `--fix` on all staged CSS files
3. Prevent the commit if any linting errors remain after auto-fixing

This ensures that all code committed to the repository adheres to the project's coding standards without requiring manual intervention.

The pre-commit hook has been optimized by removing a duplicate call to lint-staged, improving performance and ensuring each file is processed only once during the pre-commit phase.

**Section sources**
- [.husky/pre-commit](file://.husky/pre-commit)
- [package.json](file://package.json#L1-L52)

### CI/CD Integration

A GitHub Actions workflow has been added to enforce code quality standards in pull requests. The `lint.yml` workflow runs on every push and pull request to the main branch, executing the following steps:

1. Checkout the code
2. Setup Node.js environment
3. Install dependencies
4. Run ESLint, Stylelint, and Prettier checks

This prevents code that doesn't meet the quality standards from being merged into the main codebase.

``mermaid
graph TD
A[Code Push/PR] --> B[GitHub Actions]
B --> C[Checkout Code]
C --> D[Setup Node.js]
D --> E[Install Dependencies]
E --> F[Run ESLint]
F --> G[Run Stylelint]
G --> H[Run Prettier]
H --> I[Report Results]
style A fill:#3b82f6,stroke:#2563eb,color:white
style B fill:#10b981,stroke:#047857,color:white
style I fill:#f97316,stroke:#ea580c,color:white

Diagram sources

  • .github/workflows/lint.yml
  • package.json

Development Workflow Integration

The code quality tools are integrated into the development workflow through the following scripts in package.json:

"scripts": {
  "lint": "eslint \"src/**/*.{ts,js,svelte}\" --ext .ts,.js,.svelte",
  "lint:fix": "eslint \"src/**/*.{ts,js,svelte}\" --ext .ts,.js,.svelte --fix"
},
"lint-staged": {
  "*.{ts,js,svelte}": "npx eslint --cache --fix",
  "*.css": "npx stylelint --fix"
}

Developers can manually run npm run lint to check code quality or npm run lint:fix to automatically fix fixable issues. The lint-staged configuration ensures that only staged files are processed during pre-commit, making the process efficient even in large repositories.

The ESLint configuration for Svelte files has been updated to remove the project config from the Svelte ESLint parser, aligning with the latest configuration standards and improving parsing performance.

Section sources

  • package.json
  • .husky/pre-commit
  • .github/workflows/lint.yml
  • .eslintrc.cjs

Auto Device Selection

The application now supports automatic device selection with a fallback chain of CUDA → Metal → CPU, providing optimal performance across different hardware configurations. This feature is implemented through runtime detection of available hardware acceleration capabilities.

When the user selects "Auto" device preference, the system follows this priority order:

  1. CUDA: Check for NVIDIA GPU with CUDA support
  2. Metal: Check for Apple Silicon or AMD GPU with Metal support
  3. CPU: Fallback to CPU if no GPU acceleration is available

The implementation uses Candle's hardware detection utilities to check availability before attempting device initialization:

pub fn select_device(pref: Option<DevicePreference>) -> Device {
    match pref.unwrap_or(DevicePreference::Auto) {
        DevicePreference::Auto => {
            if cuda_is_available() {
                match Device::new_cuda(0) {
                    Ok(device) => {
                        println!("[device] auto-selected CUDA");
                        return device;
                    }
                    Err(e) => {
                        eprintln!("[device] CUDA init failed: {}, falling back to next option", e);
                    }
                }
            }
            
            if metal_is_available() {
                match Device::new_metal(0) {
                    Ok(device) => {
                        println!("[device] auto-selected Metal");
                        return device;
                    }
                    Err(e) => {
                        eprintln!("[device] Metal init failed: {}, falling back to CPU", e);
                        return Device::Cpu;
                    }
                }
            }
            
            println!("[device] auto-selected CPU");
            Device::Cpu
        },
        // ... other device preferences
    }
}

Section sources

  • src-tauri/src/core/device.rs
  • src-tauri/src/api/device.rs
  • src-tauri/Cargo.toml

Plugin Extensibility Architecture

The application implements a secure plugin architecture based on the IPC microservice (sidecar) model, enabling community-developed extensions without compromising core application stability.

Architecture Overview

The recommended architecture for untrusted, community-developed plugins is the IPC microservice (sidecar) model. This approach offers maximum security and stability by isolating each plugin in its own sandboxed process. A crash or vulnerability in a plugin is contained and cannot affect the main application. This model is also language-agnostic; a sidecar can be written in Python, Node.js, or any other language, as long as it communicates over a defined IPC protocol like HTTP.

graph TD
    A[Main Application] -->|HTTP/IPC| B[Plugin Sidecar 1]
    A -->|HTTP/IPC| C[Plugin Sidecar 2]
    A -->|HTTP/IPC| D[Plugin Sidecar N]
    B --> E[Python/Node.js/Rust]
    C --> F[Any Language]
    D --> G[Any Language)
    style A fill:#059669,stroke:#047857,color:white
    style B fill:#3b82f6,stroke:#2563eb,color:white
    style C fill:#3b82f6,stroke:#2563eb,color:white
    style D fill:#3b82f6,stroke:#2563eb,color:white

Security Model

The security model is based on Tauri's principle of least privilege:

  1. safetensors First: Prioritize the safetensors format which prevents arbitrary code execution
  2. Sidecar Isolation: Run all community plugins in separate, isolated child processes with minimal privileges
  3. Secure Storage: Store sensitive data like API tokens in the OS's native secure vault (macOS Keychain, Windows Credential Manager)

Plugin Registry

The long-term plan includes creating a custom plugin registry that will serve as a central repository for users to discover and manage plugins. The registry will enforce a mandatory cryptographic signing process:

  • All submitted plugins must be signed by the developer
  • The application verifies signatures before loading plugins
  • Unsigned or incorrectly signed plugins are blocked

Section sources

  • [example/High-Performance, Rust-Native LM Studio Alternative_ Strategy & Execution Blueprint for a Tauri v2 + Svelte 5 + Candle Desktop App.md](file://example/High-Performance, Rust-Native LM Studio Alternative_ Strategy & Execution Blueprint for a Tauri v2 + Svelte 5 + Candle Desktop App.md)
  • src-tauri/Cargo.toml

Referenced Files in This Document

  • src-tauri/Cargo.toml - Updated in commit b2e27e55
  • src-tauri/src/api/device.rs - Updated in commit b2e27e55
  • src-tauri/src/core/device.rs - Updated in commit b2e27e55
  • [example/High-Performance, Rust-Native LM Studio Alternative_ Strategy & Execution Blueprint for a Tauri v2 + Svelte 5 + Candle Desktop App.md](file://example/High-Performance, Rust-Native LM Studio Alternative_ Strategy & Execution Blueprint for a Tauri v2 + Svelte 5 + Candle Desktop App.md) - Added in commit 3e496854
  • src-tauri/Cargo.toml
  • src-tauri/src/lib.rs
  • src-tauri/src/models/common/model.rs
  • src-tauri/src/models/qwen3.rs
  • src-tauri/tauri.conf.json
  • vite.config.js
  • package.json
  • src-tauri/src/api/mod.rs
  • Makefile
  • src/lib/chat/codemirror-renderer.ts - Added in commit 0bbb548
  • src/lib/components/CodeMirror.svelte - Added in commit 0bbb548
  • src/lib/chat/stream/markdown_block.ts - Modified in commit 0bbb548
  • src/lib/chat/markdown.ts - Modified in commit 0bbb548
  • .eslintrc.cjs - Updated in commit cdfe07a1
  • .prettierignore - Added in commit 410ffa62
  • .eslintignore - Added in commit 410ffa62
  • .husky/pre-commit - Updated in commit cdfe07a1
  • .github/workflows/lint.yml - Added in commit 8f7e7117

Clone this wiki locally