diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ea891ac..fc3c866 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,20 +2,64 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] env: CARGO_TERM_COLOR: always RUSTFLAGS: --deny warnings RUSTDOCFLAGS: --deny warnings + SLANG_TAG: 2025.18.2 jobs: + # Check formatting. + format: + name: Format + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Run cargo fmt + run: cargo fmt --all -- --check + setup-slang: + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + include: + - os-id: linux + os: ubuntu-latest + - os-id: macos-aarch64 + os: macos-latest + runs-on: ${{ matrix.os }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + - uses: actions/cache@v4 + id: cache-slang + with: + path: ~/.cache/slang/slang-v${{ env.SLANG_TAG }}-${{ matrix.os-id }} + key: slang-v${{ env.SLANG_TAG }}-${{ matrix.os-id }} + - name: Download slang + if: steps.cache-slang.outputs.cache-hit != 'true' # Only download on miss + id: setup + run: | + ./download_slang.sh --os ${{ matrix.os-id }} --version $SLANG_TAG # Run clippy lints. clippy: + needs: setup-slang # Depends on setup-slang name: Clippy runs-on: ubuntu-latest + env: + SLANG_DIR: /home/runner/.cache/slang/slang-v2025.18.2-linux + SLANG_CACHE_KEY: slang-v2025.18.2-linux timeout-minutes: 30 steps: - name: Checkout repository @@ -29,50 +73,93 @@ jobs: - name: Install dependencies run: sudo apt-get update; sudo apt-get install --no-install-recommends libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev + - name: Retrieve Cache for Slang + uses: actions/cache/restore@v4 + with: + path: ~/.cache/slang/slang-v${{ env.SLANG_TAG }}-linux + key: slang-v${{ env.SLANG_TAG }}-linux + - name: Populate target directory from cache uses: Leafwing-Studios/cargo-cache@v2 with: sweep-cache: true - name: Run clippy lints - run: cargo clippy --locked --workspace --all-targets --all-features -- --deny warnings + run: SLANG_DIR=$SLANG_DIR cargo clippy --locked --workspace --all-targets --features web,desktop,mobile,comptime_checks -- --deny warnings - # Check formatting. - format: - name: Format + # Check documentation. + doc: + needs: setup-slang # Depends on setup-slang + name: Docs runs-on: ubuntu-latest timeout-minutes: 30 + env: + SLANG_DIR: /home/runner/.cache/slang/slang-v2025.18.2-linux + SLANG_CACHE_KEY: slang-v2025.18.2-linux steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable + + - name: Install dependencies + run: sudo apt-get update; sudo apt-get install --no-install-recommends libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev + + - name: Retrieve Cache for Slang + uses: actions/cache/restore@v4 with: - components: rustfmt + path: ~/.cache/slang/slang-v${{ env.SLANG_TAG }}-linux + key: ${{ env.SLANG_CACHE_KEY }} - - name: Run cargo fmt - run: cargo fmt --all -- --check + - name: Populate target directory from cache + uses: Leafwing-Studios/cargo-cache@v2 + with: + sweep-cache: true - # Check documentation. - doc: - name: Docs - runs-on: ubuntu-latest + - name: Check documentation + run: SLANG_DIR=$SLANG_DIR cargo doc --locked --workspace --features web,desktop,mobile,comptime_checks --document-private-items --no-deps + # Testing. + testing: + needs: setup-slang + name: Tests + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + include: + - slang-dir: /home/runner/.cache/slang/slang-v2025.18.2-linux + os: ubuntu-latest + - slang-dir: /Users/runner/.cache/slang/slang-v2025.18.2-macos-aarch64 + os: macos-latest + - slang-cache-key: slang-v2025.18.2-linux + os: ubuntu-latest + - slang-cache-key: slang-v2025.18.2-macos-aarch64 + os: macos-latest + runs-on: ${{ matrix.os }} + env: + SLANG_DIR: ${{ matrix.slang-dir }} + SLANG_CACHE_KEY: ${{ matrix.slang-cache-key }} timeout-minutes: 30 steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - - name: Install dependencies + if: ${{ runner.os == 'linux' }} run: sudo apt-get update; sudo apt-get install --no-install-recommends libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev + - name: Retrieve Cache for Slang + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: true + path: ~/.cache/slang/${{ matrix.slang-cache-key }} + key: ${{ matrix.slang-cache-key }} - name: Populate target directory from cache uses: Leafwing-Studios/cargo-cache@v2 with: sweep-cache: true - - - name: Check documentation - run: cargo doc --locked --workspace --all-features --document-private-items --no-deps \ No newline at end of file + - name: Run Cargo Tests + run: | + SLANG_DIR=$SLANG_DIR cargo test --verbose diff --git a/.gitignore b/.gitignore index 96c4569..0fe4180 100644 --- a/.gitignore +++ b/.gitignore @@ -111,4 +111,6 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser -.DS_store \ No newline at end of file +.DS_store + +downloaded_slang \ No newline at end of file diff --git a/crates/slai-chat/README.md b/crates/slai-chat/README.md index cbac86f..805515c 100644 --- a/crates/slai-chat/README.md +++ b/crates/slai-chat/README.md @@ -5,11 +5,18 @@ This is a basic chat interface for testing **slai**. Currently only supports: - Qwen 2. - Segment Anything. -In order to compile and run `slai-chat`, be sure to define the `SLANG_DIR` environment variable: -1. Download the Slang compiler libraries for your platform: https://github.com/shader-slang/slang/releases/tag/v2025.16 -2. Unzip the downloaded directory, and use its path as value to the `SLANG_DIR` environment variable: `SLANG_DIR=/path/to/slang`. +In order to compile and run `slai-chat`, be sure to define the `SLANG_DIR` environment variable. + +Either: + +- Run the script at the root of slai repository: `SLANG_DIR=$(./download_slang.sh | grep '^SLANG_DIR=' | cut -d'=' -f2-)` +- Or manually: + 1. Download the Slang compiler libraries for your platform: https://github.com/shader-slang/slang/releases/tag/v2025.16 + 2. Unzip the downloaded directory, and use its path as value to the `SLANG_DIR` environment variable: `SLANG_DIR=/path/to/slang`. Note that the variable must point to the root of the slang installation (i.e. the directory that contains `bin` and `lib`). + + To run the GUI natively: ```bash dx run --release --features desktop diff --git a/crates/slai-chat/src/chat_llama2.rs b/crates/slai-chat/src/chat_llama2.rs index ae01507..2cf9b57 100644 --- a/crates/slai-chat/src/chat_llama2.rs +++ b/crates/slai-chat/src/chat_llama2.rs @@ -178,10 +178,7 @@ impl ChatLlama2 { // Run the transformer. let mut encoder = backend.begin_encoding(); backend.write_buffer(state.rope_config_mut().buffer_mut(), &[rope_config])?; - backend.write_buffer( - state.rms_norm_config_mut().buffer_mut(), - &[rms_norm_config], - )?; + backend.write_buffer(state.rms_norm_config_mut().buffer_mut(), &[rms_norm_config])?; backend.write_buffer(state.attn_params_mut().buffer_mut(), &[attn_params])?; state .x diff --git a/crates/slai-chat/src/components/chat.rs b/crates/slai-chat/src/components/chat.rs index a89131c..a88db42 100644 --- a/crates/slai-chat/src/components/chat.rs +++ b/crates/slai-chat/src/components/chat.rs @@ -437,18 +437,16 @@ fn submit_prompt( let gpu = gpu.clone(); let llm = model.llm.clone(); execute(async move { - llm - .forward( - &gpu.backend, - prompt, - sampler, - chat_template, - next_pos, - |msg| { - Ok(out.unbounded_send(msg)?) - }) - .await - .unwrap() + llm.forward( + &gpu.backend, + prompt, + sampler, + chat_template, + next_pos, + |msg| Ok(out.unbounded_send(msg)?), + ) + .await + .unwrap() }); } diff --git a/crates/slai-chat/src/components/home.rs b/crates/slai-chat/src/components/home.rs index d82ca69..c2e517a 100644 --- a/crates/slai-chat/src/components/home.rs +++ b/crates/slai-chat/src/components/home.rs @@ -1,7 +1,6 @@ use crate::chat_template::ChatTemplate; use crate::llm::ChatLlm; use crate::{GgufMetadata, GpuInstanceCtx, LoadedModel, LoadedModelSignal, SelectedBackend}; -use async_std::sync::RwLock; use dioxus::prelude::*; use dioxus_markdown::Markdown; use rfd::FileHandle; diff --git a/crates/slai-chat/src/main.rs b/crates/slai-chat/src/main.rs index 8c73cc3..21adc8e 100644 --- a/crates/slai-chat/src/main.rs +++ b/crates/slai-chat/src/main.rs @@ -3,7 +3,6 @@ use crate::cli::Cli; use crate::llm::ChatLlm; use crate::prompt::Prompt; use crate::sampler::SamplerParams; -use async_std::sync::RwLock; use components::{Chat, Home}; use dioxus::prelude::*; use slang_hal::backend::Backend; @@ -161,7 +160,7 @@ fn App() -> Element { match (&*gpu_wgpu.read_unchecked(), &*gpu_cuda.read_unchecked()) { (Some(Ok(gpu_wgpu)), Some(Ok(gpu_cuda))) => { use_context_provider(|| gpu_wgpu.clone()); - use_context_provider(|| gpu_cuda.clone()); + use_context_provider(|| *gpu_cuda); use_context_provider(|| LoadedModelSignal::new(None)); use_context_provider(|| Signal::new(PromptState::default())); diff --git a/crates/slai/build.rs b/crates/slai/build.rs index 6f0ecf5..293e726 100644 --- a/crates/slai/build.rs +++ b/crates/slai/build.rs @@ -24,7 +24,7 @@ pub fn main() { let out_dir = Path::new(&out_dir); let autogen_target = out_dir.join("autogen"); - for (target, ext) in targets { + for (target, _ext) in targets { std::fs::create_dir_all(&autogen_target).unwrap(); slang.compile_all(target, "./shaders", &autogen_target, &[]); } diff --git a/download_slang.sh b/download_slang.sh new file mode 100755 index 0000000..4cad8dc --- /dev/null +++ b/download_slang.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# Default values +OS="" +OUTPUT_DIR="$HOME/.cache/slang" +SLANG_VERSION="" +SLANG_TAG="" +ASSET_SUFFIX="" +SLANG_URL_BASE="https://github.com/shader-slang/slang/releases/download" + +# Help message +usage() { + echo "Usage: $0 [--os ] [--output-dir ] [--version ]" + echo " --os: Target OS (default: auto-detect from current platform)" + echo " --output-dir: Directory to extract Slang (default: ~/.cache/slang)" + echo " --version: Slang version (e.g., 2025.18.2, default: latest)" + echo " --slang-dir-name: Custom name for the Slang directory (default: slang-v-)" + echo "Example: $0 --os linux --output-dir /tmp/slang --version 2025.18.2 --slang-dir-name my-slang-2025.18.2-linux" +} + +# Parse arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --os) OS="$2"; shift ;; + --output-dir) export OUTPUT_DIR="$2"; shift ;; + --version) export SLANG_VERSION="$2"; shift ;; + --slang-dir-name) SLANG_DIR_NAME="$2"; shift ;; + *) usage ; exit 1 ;; + esac + shift +done + +# Detect OS if not specified +if [[ -z "$OS" ]]; then + case "$(uname -s)" in + Linux*) OS="linux" ;; + Darwin*) + if [[ "$(uname -m)" == "arm64" ]]; then + OS="macos-aarch64" + else + OS="macos" + fi + ;; + CYGWIN*|MINGW*|MSYS*) OS="windows" ;; + *) echo "Error: Unable to detect OS. Specify --os (linux, macos, macos-arm64, windows)"; exit 1 ;; + esac +fi + +# Determine asset suffix based on OS +case "$OS" in + linux) ASSET_SUFFIX="linux-x86_64.zip" ;; + macos) ASSET_SUFFIX="macos-x86_64.zip" ;; + macos-aarch64) ASSET_SUFFIX="macos-aarch64.zip" ;; + windows) ASSET_SUFFIX="windows-x86_64.zip" ;; + *) echo "Error: Unsupported OS: $OS"; exit 1 ;; +esac + +# Get Slang version if not specified +if [[ -z "$SLANG_VERSION" ]]; then + export SLANG_TAG=$(curl -s https://api.github.com/repos/shader-slang/slang/releases/latest | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') + export SLANG_VERSION=$(echo "$SLANG_TAG" | sed 's/v//') # e.g., v2025.18.2 -> 2025.18.2 +else + export SLANG_TAG="v$SLANG_VERSION" +fi + +if [[ -z "$SLANG_VERSION" ]]; then + echo "Error: Could not determine Slang version" + exit 1 +fi + +# Set up paths - use custom name if provided, otherwise use default +if [[ -z "$SLANG_DIR_NAME" ]]; then + SLANG_DIR_NAME="slang-v$SLANG_VERSION-$OS" +fi + + +# Set up paths +SLANG_DIR="$OUTPUT_DIR/$SLANG_DIR_NAME" +ZIP_URL="$SLANG_URL_BASE/$SLANG_TAG/slang-$SLANG_VERSION-$ASSET_SUFFIX" +TEMP_ZIP="/tmp/slang-$SLANG_VERSION.zip" + +# Check if Slang is already extracted +if [[ -d "$SLANG_DIR" ]] && [[ -f "$SLANG_DIR/bin/slangc" || -f "$SLANG_DIR/bin/slangc.exe" ]]; then + echo "Using existing Slang at $SLANG_DIR" + echo "SLANG_DIR=$SLANG_DIR" + exit 0 +fi + +# Download Slang release +echo "Downloading Slang v$SLANG_VERSION for $OS from $ZIP_URL..." +mkdir -p "$OUTPUT_DIR" +curl -L -o "$TEMP_ZIP" "$ZIP_URL" || { echo "Error: Download failed for $ZIP_URL"; exit 1; } + +# Extract based on OS +echo "Extracting to $SLANG_DIR..." +if [[ "$OS" == "windows" ]]; then + # Windows: Assume 7z is available (or adjust for PowerShell/Expand-Archive) + 7z x "$TEMP_ZIP" -o"$SLANG_DIR" -y > /dev/null || { echo "Error: Extraction failed"; rm -f "$TEMP_ZIP"; exit 1; } +else + # Linux/macOS: Use unzip + unzip -q "$TEMP_ZIP" -d "$SLANG_DIR" || { echo "Error: Extraction failed"; rm -f "$TEMP_ZIP"; exit 1; } +fi + +# Clean up +rm -f "$TEMP_ZIP" + +# Verify extraction +if [[ ! -f "$SLANG_DIR/bin/slangc" && ! -f "$SLANG_DIR/bin/slangc.exe" ]]; then + echo "Error: Extraction incomplete, slangc not found in $SLANG_DIR/bin" + exit 1 +fi + +echo "Slang v$SLANG_VERSION extracted to $SLANG_DIR" +echo "SLANG_DIR=$SLANG_DIR" + +# For use in calling script +export SLANG_DIR \ No newline at end of file