From 40fdba13c02c04e6794d6f752bced9f1871b92a3 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Mon, 15 Dec 2025 23:15:22 -0800 Subject: [PATCH 01/19] Add GitHub Actions CI/CD workflow for multi-platform builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set up automated builds and tests for Linux, macOS, and Windows using GitHub Actions. The workflow runs on pushes to main and pull requests to main, building Tauri application bundles for all three platforms and running Rust tests. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/build.yml | 103 ++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c9fc100 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,103 @@ +name: Build and Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + strategy: + fail-fast: false + matrix: + platform: + - os: ubuntu-22.04 + rust_target: x86_64-unknown-linux-gnu + - os: macos-latest + rust_target: aarch64-apple-darwin + - os: windows-latest + rust_target: x86_64-pc-windows-msvc + + runs-on: ${{ matrix.platform.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies (Ubuntu) + if: matrix.platform.os == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + build-essential \ + curl \ + wget \ + file \ + libssl-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.platform.rust_target }} + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri -> target + + - name: Install frontend dependencies + run: npm ci + + - name: Run Rust tests + working-directory: src-tauri + run: cargo test --verbose + + - name: Build frontend + run: npm run build + + - name: Build Tauri application + run: npm run tauri build + + - name: Upload artifacts (Linux) + if: matrix.platform.os == 'ubuntu-22.04' + uses: actions/upload-artifact@v4 + with: + name: linux-build + path: | + src-tauri/target/release/bundle/deb/*.deb + src-tauri/target/release/bundle/appimage/*.AppImage + if-no-files-found: warn + + - name: Upload artifacts (macOS) + if: matrix.platform.os == 'macos-latest' + uses: actions/upload-artifact@v4 + with: + name: macos-build + path: | + src-tauri/target/release/bundle/dmg/*.dmg + src-tauri/target/release/bundle/macos/*.app + if-no-files-found: warn + + - name: Upload artifacts (Windows) + if: matrix.platform.os == 'windows-latest' + uses: actions/upload-artifact@v4 + with: + name: windows-build + path: | + src-tauri/target/release/bundle/msi/*.msi + src-tauri/target/release/bundle/nsis/*.exe + if-no-files-found: warn From 4c751aeca46543d148438ccb01cb3d867f2f16c3 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 16 Dec 2025 06:45:52 -0800 Subject: [PATCH 02/19] Download llama.cpp pre-built libraries from GitHub releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add steps to download the latest llama.cpp release binaries for each platform: - Linux: ubuntu-x64 (CPU) - macOS: macos-arm64 (Metal) - Windows: win-vulkan-x64 (Vulkan) Libraries are extracted and copied to the appropriate src-tauri/libs// directories before building. This avoids the need to commit large binary files to the repository or build llama.cpp from source during CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/build.yml | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c9fc100..1972e5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,6 +59,44 @@ jobs: with: workspaces: src-tauri -> target + - name: Download llama.cpp libraries (Linux) + if: matrix.platform.os == 'ubuntu-22.04' + run: | + RELEASE_TAG=$(curl -s https://api.github.com/repos/ggerganov/llama.cpp/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + wget https://github.com/ggerganov/llama.cpp/releases/download/${RELEASE_TAG}/llama-${RELEASE_TAG}-bin-ubuntu-x64.tar.gz + tar -xzf llama-${RELEASE_TAG}-bin-ubuntu-x64.tar.gz + mkdir -p src-tauri/libs/linux-x64 + find llama-${RELEASE_TAG}-bin-ubuntu-x64 -name "*.so*" -exec cp {} src-tauri/libs/linux-x64/ \; + ls -la src-tauri/libs/linux-x64/ + + - name: Download llama.cpp libraries (macOS) + if: matrix.platform.os == 'macos-latest' + run: | + RELEASE_TAG=$(curl -s https://api.github.com/repos/ggerganov/llama.cpp/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + wget https://github.com/ggerganov/llama.cpp/releases/download/${RELEASE_TAG}/llama-${RELEASE_TAG}-bin-macos-arm64.tar.gz + tar -xzf llama-${RELEASE_TAG}-bin-macos-arm64.tar.gz + mkdir -p src-tauri/libs/darwin-arm64 + find llama-${RELEASE_TAG}-bin-macos-arm64 -name "*.dylib" -exec cp {} src-tauri/libs/darwin-arm64/ \; + ls -la src-tauri/libs/darwin-arm64/ + + - name: Download llama.cpp libraries (Windows) + if: matrix.platform.os == 'windows-latest' + shell: powershell + run: | + $release = Invoke-RestMethod -Uri "https://api.github.com/repos/ggerganov/llama.cpp/releases/latest" + $tag = $release.tag_name + $url = "https://github.com/ggerganov/llama.cpp/releases/download/$tag/llama-$tag-bin-win-vulkan-x64.zip" + Invoke-WebRequest -Uri $url -OutFile "llama-libs.zip" + Expand-Archive -Path "llama-libs.zip" -DestinationPath "llama-libs" + New-Item -ItemType Directory -Force -Path src-tauri/libs/windows-x64-vulkan + Get-ChildItem -Path llama-libs -Recurse -Filter "*.dll" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ + Get-ChildItem src-tauri/libs/windows-x64-vulkan/ + + - name: Set Windows backend environment + if: matrix.platform.os == 'windows-latest' + shell: powershell + run: echo "LLAMA_BACKEND=vulkan" >> $env:GITHUB_ENV + - name: Install frontend dependencies run: npm ci From fd526814a6c803f14b932e22f4971c80a5b50097 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 16 Dec 2025 06:53:23 -0800 Subject: [PATCH 03/19] Fix llama.cpp download to use ggml-org repo and pin to b7423 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update workflow to download from the correct repository (ggml-org/llama.cpp) and pin to the specific release version b7423 instead of dynamically fetching the latest version. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/build.yml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1972e5e..7af3899 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,31 +62,26 @@ jobs: - name: Download llama.cpp libraries (Linux) if: matrix.platform.os == 'ubuntu-22.04' run: | - RELEASE_TAG=$(curl -s https://api.github.com/repos/ggerganov/llama.cpp/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - wget https://github.com/ggerganov/llama.cpp/releases/download/${RELEASE_TAG}/llama-${RELEASE_TAG}-bin-ubuntu-x64.tar.gz - tar -xzf llama-${RELEASE_TAG}-bin-ubuntu-x64.tar.gz + wget https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-ubuntu-x64.tar.gz + tar -xzf llama-b7423-bin-ubuntu-x64.tar.gz mkdir -p src-tauri/libs/linux-x64 - find llama-${RELEASE_TAG}-bin-ubuntu-x64 -name "*.so*" -exec cp {} src-tauri/libs/linux-x64/ \; + find llama-b7423-bin-ubuntu-x64 -name "*.so*" -exec cp {} src-tauri/libs/linux-x64/ \; ls -la src-tauri/libs/linux-x64/ - name: Download llama.cpp libraries (macOS) if: matrix.platform.os == 'macos-latest' run: | - RELEASE_TAG=$(curl -s https://api.github.com/repos/ggerganov/llama.cpp/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - wget https://github.com/ggerganov/llama.cpp/releases/download/${RELEASE_TAG}/llama-${RELEASE_TAG}-bin-macos-arm64.tar.gz - tar -xzf llama-${RELEASE_TAG}-bin-macos-arm64.tar.gz + wget https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-macos-arm64.tar.gz + tar -xzf llama-b7423-bin-macos-arm64.tar.gz mkdir -p src-tauri/libs/darwin-arm64 - find llama-${RELEASE_TAG}-bin-macos-arm64 -name "*.dylib" -exec cp {} src-tauri/libs/darwin-arm64/ \; + find llama-b7423-bin-macos-arm64 -name "*.dylib" -exec cp {} src-tauri/libs/darwin-arm64/ \; ls -la src-tauri/libs/darwin-arm64/ - name: Download llama.cpp libraries (Windows) if: matrix.platform.os == 'windows-latest' shell: powershell run: | - $release = Invoke-RestMethod -Uri "https://api.github.com/repos/ggerganov/llama.cpp/releases/latest" - $tag = $release.tag_name - $url = "https://github.com/ggerganov/llama.cpp/releases/download/$tag/llama-$tag-bin-win-vulkan-x64.zip" - Invoke-WebRequest -Uri $url -OutFile "llama-libs.zip" + Invoke-WebRequest -Uri "https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-win-vulkan-x64.zip" -OutFile "llama-libs.zip" Expand-Archive -Path "llama-libs.zip" -DestinationPath "llama-libs" New-Item -ItemType Directory -Force -Path src-tauri/libs/windows-x64-vulkan Get-ChildItem -Path llama-libs -Recurse -Filter "*.dll" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ From 5ef8f77f04eca8936760766cc1dcf637e089d4c7 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 16 Dec 2025 06:57:39 -0800 Subject: [PATCH 04/19] Fix directory path for llama.cpp library extraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All llama.cpp release archives extract to the 'llama-b7423' directory, not the full archive filename. Updated all three platform steps to use the correct extracted directory name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7af3899..4c090d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,7 @@ jobs: wget https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-ubuntu-x64.tar.gz tar -xzf llama-b7423-bin-ubuntu-x64.tar.gz mkdir -p src-tauri/libs/linux-x64 - find llama-b7423-bin-ubuntu-x64 -name "*.so*" -exec cp {} src-tauri/libs/linux-x64/ \; + find llama-b7423 -name "*.so*" -exec cp {} src-tauri/libs/linux-x64/ \; ls -la src-tauri/libs/linux-x64/ - name: Download llama.cpp libraries (macOS) @@ -74,7 +74,7 @@ jobs: wget https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-macos-arm64.tar.gz tar -xzf llama-b7423-bin-macos-arm64.tar.gz mkdir -p src-tauri/libs/darwin-arm64 - find llama-b7423-bin-macos-arm64 -name "*.dylib" -exec cp {} src-tauri/libs/darwin-arm64/ \; + find llama-b7423 -name "*.dylib" -exec cp {} src-tauri/libs/darwin-arm64/ \; ls -la src-tauri/libs/darwin-arm64/ - name: Download llama.cpp libraries (Windows) @@ -82,9 +82,9 @@ jobs: shell: powershell run: | Invoke-WebRequest -Uri "https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-win-vulkan-x64.zip" -OutFile "llama-libs.zip" - Expand-Archive -Path "llama-libs.zip" -DestinationPath "llama-libs" + Expand-Archive -Path "llama-libs.zip" -DestinationPath "." New-Item -ItemType Directory -Force -Path src-tauri/libs/windows-x64-vulkan - Get-ChildItem -Path llama-libs -Recurse -Filter "*.dll" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ + Get-ChildItem -Path llama-b7423 -Recurse -Filter "*.dll" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ Get-ChildItem src-tauri/libs/windows-x64-vulkan/ - name: Set Windows backend environment From 5754afd378294303a71e756d37956bfb74e7dcb1 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 16 Dec 2025 07:08:34 -0800 Subject: [PATCH 05/19] Use cp -P to preserve symlinks when copying llama.cpp libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change from find with -exec to direct cp -P which preserves symlinks properly. This ensures both versioned libraries (libllama.0.dylib) and their symlinks (libllama.dylib) are copied correctly for the linker to find them. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c090d2..57f38d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,7 @@ jobs: wget https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-ubuntu-x64.tar.gz tar -xzf llama-b7423-bin-ubuntu-x64.tar.gz mkdir -p src-tauri/libs/linux-x64 - find llama-b7423 -name "*.so*" -exec cp {} src-tauri/libs/linux-x64/ \; + cp -P llama-b7423/*.so* src-tauri/libs/linux-x64/ 2>/dev/null || true ls -la src-tauri/libs/linux-x64/ - name: Download llama.cpp libraries (macOS) @@ -74,7 +74,7 @@ jobs: wget https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-macos-arm64.tar.gz tar -xzf llama-b7423-bin-macos-arm64.tar.gz mkdir -p src-tauri/libs/darwin-arm64 - find llama-b7423 -name "*.dylib" -exec cp {} src-tauri/libs/darwin-arm64/ \; + cp -P llama-b7423/*.dylib src-tauri/libs/darwin-arm64/ ls -la src-tauri/libs/darwin-arm64/ - name: Download llama.cpp libraries (Windows) @@ -84,7 +84,7 @@ jobs: Invoke-WebRequest -Uri "https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-win-vulkan-x64.zip" -OutFile "llama-libs.zip" Expand-Archive -Path "llama-libs.zip" -DestinationPath "." New-Item -ItemType Directory -Force -Path src-tauri/libs/windows-x64-vulkan - Get-ChildItem -Path llama-b7423 -Recurse -Filter "*.dll" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ + Copy-Item -Path "llama-b7423/*.dll" -Destination src-tauri/libs/windows-x64-vulkan/ Get-ChildItem src-tauri/libs/windows-x64-vulkan/ - name: Set Windows backend environment From 6989197ac5d43d405e2b1e74157dffed01bae604 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 16 Dec 2025 07:19:35 -0800 Subject: [PATCH 06/19] Set library path environment variables for Rust tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add platform-specific environment variables so test binaries can find the llama.cpp libraries at runtime: - Linux: LD_LIBRARY_PATH - macOS: DYLD_LIBRARY_PATH - Windows: PATH This ensures the dynamic linker can locate libllama, libmtmd, and libggml when running cargo test. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/build.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 57f38d2..7e52cb8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -95,8 +95,25 @@ jobs: - name: Install frontend dependencies run: npm ci - - name: Run Rust tests + - name: Run Rust tests (Linux) + if: matrix.platform.os == 'ubuntu-22.04' + working-directory: src-tauri + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/src-tauri/libs/linux-x64 + run: cargo test --verbose + + - name: Run Rust tests (macOS) + if: matrix.platform.os == 'macos-latest' + working-directory: src-tauri + env: + DYLD_LIBRARY_PATH: ${{ github.workspace }}/src-tauri/libs/darwin-arm64 + run: cargo test --verbose + + - name: Run Rust tests (Windows) + if: matrix.platform.os == 'windows-latest' working-directory: src-tauri + env: + PATH: ${{ github.workspace }}\src-tauri\libs\windows-x64-vulkan;${{ env.PATH }} run: cargo test --verbose - name: Build frontend From a6c86cec577125c573d2c74c6b537f9eb382ebf6 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 16 Dec 2025 07:21:31 -0800 Subject: [PATCH 07/19] Configure library bundling for all platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Tauri configuration to bundle llama.cpp libraries with production binaries: - macOS: Already configured to bundle dylibs into Frameworks - Linux: Added resources config to bundle .so files alongside binary Updated RPATH to use $ORIGIN so binary finds libs at runtime - Windows: Added resources config to bundle DLLs alongside executable This ensures distributed binaries can find and load llama.cpp libraries without requiring users to install them separately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src-tauri/build.rs | 7 ++++++- src-tauri/tauri.conf.json | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src-tauri/build.rs b/src-tauri/build.rs index f036fc8..9ec40e9 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -44,7 +44,12 @@ fn main() { println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks"); #[cfg(target_os = "linux")] - println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display()); + { + // Use $ORIGIN to reference the directory containing the executable + // Libraries will be bundled in the same directory as the binary + println!("cargo:rustc-link-arg=-Wl,-rpath,$ORIGIN"); + println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display()); + } // Re-run build script if libraries change println!("cargo:rerun-if-changed=libs/{}", lib_subdir); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 3b88705..901b9ce 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -54,12 +54,29 @@ "icons/icon.icns", "icons/icon.ico" ], + "resources": { + "libs/linux-x64/*.so*": "../", + "libs/windows-x64-vulkan/*.dll": "../" + }, "macOS": { "frameworks": [ "libs/darwin-arm64/libllama.0.dylib", "libs/darwin-arm64/libmtmd.0.dylib", "libs/darwin-arm64/libggml.0.dylib" ] + }, + "linux": { + "deb": { + "depends": [] + } + }, + "windows": { + "wix": { + "language": "en-US" + }, + "nsis": { + "languages": ["English"] + } } } } From 0a4df37203a9596be553e3a3ecdcd4dda2cb187f Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 16 Dec 2025 07:26:44 -0800 Subject: [PATCH 08/19] Fix Tauri resources configuration syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change resources from object to array format, which is the correct Tauri v2 syntax. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src-tauri/tauri.conf.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 901b9ce..a8e6b8b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -54,10 +54,10 @@ "icons/icon.icns", "icons/icon.ico" ], - "resources": { - "libs/linux-x64/*.so*": "../", - "libs/windows-x64-vulkan/*.dll": "../" - }, + "resources": [ + "libs/linux-x64/*", + "libs/windows-x64-vulkan/*" + ], "macOS": { "frameworks": [ "libs/darwin-arm64/libllama.0.dylib", From 655dbbe26484b46afa83e3bb9256666a5cb4676d Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Wed, 17 Dec 2025 07:08:04 -0800 Subject: [PATCH 09/19] Copy libraries to common resources dir for bundling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a src-tauri/resources directory and copy platform-specific libraries there before building. This avoids Tauri errors about missing platform-specific paths while ensuring libraries are bundled into the final application. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/build.yml | 13 +++++++++++++ src-tauri/tauri.conf.json | 3 +-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e52cb8..96c5faa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -119,6 +119,19 @@ jobs: - name: Build frontend run: npm run build + - name: Copy libraries for bundling (Linux) + if: matrix.platform.os == 'ubuntu-22.04' + run: | + mkdir -p src-tauri/resources + cp -P src-tauri/libs/linux-x64/* src-tauri/resources/ + + - name: Copy libraries for bundling (Windows) + if: matrix.platform.os == 'windows-latest' + shell: powershell + run: | + New-Item -ItemType Directory -Force -Path src-tauri/resources + Copy-Item -Path src-tauri/libs/windows-x64-vulkan/* -Destination src-tauri/resources/ + - name: Build Tauri application run: npm run tauri build diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a8e6b8b..ad4bc91 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -55,8 +55,7 @@ "icons/icon.ico" ], "resources": [ - "libs/linux-x64/*", - "libs/windows-x64-vulkan/*" + "resources/*" ], "macOS": { "frameworks": [ From abce9fb3bf5e91a72af2490df448b34da1bb7732 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Wed, 17 Dec 2025 07:14:13 -0800 Subject: [PATCH 10/19] Getting rid of resource bundling for now --- src-tauri/tauri.conf.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ad4bc91..671bdf8 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -54,9 +54,6 @@ "icons/icon.icns", "icons/icon.ico" ], - "resources": [ - "resources/*" - ], "macOS": { "frameworks": [ "libs/darwin-arm64/libllama.0.dylib", From 480607a28d953a92e5c5dffdbbede8f9294b21b8 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 18 Dec 2025 22:50:50 -0800 Subject: [PATCH 11/19] Skip model-dependent tests in CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests that require SmolVLM model files now check for CI environment variable and skip automatically in GitHub Actions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src-tauri/tests/contextual_crop_test.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src-tauri/tests/contextual_crop_test.rs b/src-tauri/tests/contextual_crop_test.rs index 6ef2a2b..53d85f3 100644 --- a/src-tauri/tests/contextual_crop_test.rs +++ b/src-tauri/tests/contextual_crop_test.rs @@ -22,6 +22,11 @@ fn create_solid_color_image(width: u32, height: u32, r: u8, g: u8, b: u8) -> Vec #[test] fn test_multi_turn_conversation_same_image() { + if std::env::var("CI").is_ok() { + println!("Skipping test in CI - requires SmolVLM model files"); + return; + } + let (model_path, mmproj_path) = get_model_paths(); println!("Loading model..."); @@ -85,6 +90,11 @@ fn test_multi_turn_conversation_same_image() { #[test] fn test_contextual_crop_workflow() { + if std::env::var("CI").is_ok() { + println!("Skipping test in CI - requires SmolVLM model files"); + return; + } + let (model_path, mmproj_path) = get_model_paths(); println!("Loading model for contextual crop test..."); From cbbc679e2051aba5bb8d1648308d2e61b3017a7b Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 18 Dec 2025 23:02:01 -0800 Subject: [PATCH 12/19] Skip all model-dependent tests in CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added CI environment check to all tests that require SmolVLM model files to be present locally. These tests will now skip automatically in GitHub Actions while still running locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src-tauri/tests/inference_integration_test.rs | 10 ++++++++++ src-tauri/tests/inference_test.rs | 10 ++++++++++ src-tauri/tests/packraft_test.rs | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/src-tauri/tests/inference_integration_test.rs b/src-tauri/tests/inference_integration_test.rs index 67d63b7..75055ec 100644 --- a/src-tauri/tests/inference_integration_test.rs +++ b/src-tauri/tests/inference_integration_test.rs @@ -2,6 +2,11 @@ use baseweightcanvas_lib::inference_engine::InferenceEngine; #[test] fn test_model_loading() { + if std::env::var("CI").is_ok() { + println!("Skipping test in CI - requires SmolVLM model files"); + return; + } + let model_path = "/home/bowserj/.local/share/canvas/models/smolvlm2-2.2b-instruct/SmolVLM2-2.2B-Instruct-Q4_K_M.gguf"; let mmproj_path = "/home/bowserj/.local/share/canvas/models/smolvlm2-2.2b-instruct/mmproj-SmolVLM2-2.2B-Instruct-f16.gguf"; @@ -14,6 +19,11 @@ fn test_model_loading() { #[test] fn test_inference_with_blue_image() { + if std::env::var("CI").is_ok() { + println!("Skipping test in CI - requires SmolVLM model files"); + return; + } + let model_path = "/home/bowserj/.local/share/canvas/models/smolvlm2-2.2b-instruct/SmolVLM2-2.2B-Instruct-Q4_K_M.gguf"; let mmproj_path = "/home/bowserj/.local/share/canvas/models/smolvlm2-2.2b-instruct/mmproj-SmolVLM2-2.2B-Instruct-f16.gguf"; diff --git a/src-tauri/tests/inference_test.rs b/src-tauri/tests/inference_test.rs index b7703ec..4ac3ba8 100644 --- a/src-tauri/tests/inference_test.rs +++ b/src-tauri/tests/inference_test.rs @@ -17,6 +17,11 @@ fn get_model_paths() -> (String, String) { #[test] fn test_model_loading() { + if std::env::var("CI").is_ok() { + println!("Skipping test in CI - requires SmolVLM model files"); + return; + } + let (model_path, mmproj_path) = get_model_paths(); println!("Loading model from: {}", model_path); @@ -28,6 +33,11 @@ fn test_model_loading() { #[test] fn test_simple_inference() { + if std::env::var("CI").is_ok() { + println!("Skipping test in CI - requires SmolVLM model files"); + return; + } + let (model_path, mmproj_path) = get_model_paths(); println!("Loading model from: {}", model_path); diff --git a/src-tauri/tests/packraft_test.rs b/src-tauri/tests/packraft_test.rs index 9d04660..7a00d21 100644 --- a/src-tauri/tests/packraft_test.rs +++ b/src-tauri/tests/packraft_test.rs @@ -3,6 +3,11 @@ use image::GenericImageView; #[test] fn test_packraft_image_inference() { + if std::env::var("CI").is_ok() { + println!("Skipping test in CI - requires SmolVLM model files"); + return; + } + let model_path = "/home/bowserj/.local/share/canvas/models/smolvlm2-2.2b-instruct/SmolVLM2-2.2B-Instruct-Q4_K_M.gguf"; let mmproj_path = "/home/bowserj/.local/share/canvas/models/smolvlm2-2.2b-instruct/mmproj-SmolVLM2-2.2B-Instruct-f16.gguf"; let image_path = "/home/bowserj/packraft.jpg"; From cc9b337d4db39afd384a257ef8ce0ddcc3f8ebb3 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 18 Dec 2025 23:10:39 -0800 Subject: [PATCH 13/19] Fix FFI struct definitions in llama_inference.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LlamaModelParams, LlamaContextParams, and MtmdContextParams structs were simplified/incorrect and didn't match the actual C ABI from llama.cpp. This caused stack corruption and SIGSEGV on macOS when calling llama_model_default_params() which returns a 72-byte struct. Updated to match the correct definitions from inference_engine.rs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src-tauri/src/llama_inference.rs | 45 +++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/llama_inference.rs b/src-tauri/src/llama_inference.rs index 793fdf3..7dcb8c5 100644 --- a/src-tauri/src/llama_inference.rs +++ b/src-tauri/src/llama_inference.rs @@ -40,18 +40,57 @@ pub struct MtmdInputText { #[repr(C)] pub struct LlamaModelParams { - // Simplified - add fields as needed + // Pointers come first + devices: *mut c_void, + tensor_buft_overrides: *const c_void, + // Then the fields we care about n_gpu_layers: c_int, + split_mode: c_int, + main_gpu: c_int, + tensor_split: *const c_float, + progress_callback: *const c_void, + progress_callback_user_data: *mut c_void, + kv_overrides: *const c_void, + // Booleans at the end + vocab_only: bool, use_mmap: bool, use_mlock: bool, + check_tensors: bool, + // Padding to reach 72 bytes + _padding: [u8; 4], } #[repr(C)] pub struct LlamaContextParams { n_ctx: u32, n_batch: u32, + n_ubatch: u32, + n_seq_max: u32, n_threads: c_int, + n_threads_batch: c_int, + rope_scaling_type: c_int, + pooling_type: c_int, + attention_type: c_int, + flash_attn_type: c_int, + rope_freq_base: c_float, + rope_freq_scale: c_float, + yarn_ext_factor: c_float, + yarn_attn_factor: c_float, + yarn_beta_fast: c_float, + yarn_beta_slow: c_float, + yarn_orig_ctx: u32, + defrag_thold: c_float, + cb_eval: *const c_void, + cb_eval_user_data: *mut c_void, + type_k: c_int, + type_v: c_int, + logits_all: bool, + embeddings: bool, + offload_kqv: bool, flash_attn: bool, + no_perf: bool, + abort_callback: *const c_void, + abort_callback_data: *mut c_void, } #[repr(C)] @@ -59,7 +98,11 @@ pub struct MtmdContextParams { use_gpu: bool, print_timings: bool, n_threads: c_int, + image_marker: *const c_char, media_marker: *const c_char, + flash_attn_type: c_int, + image_min_tokens: c_int, + image_max_tokens: c_int, } pub type LlamaToken = i32; From 6ad3e2b5b8489bf388f6f7d26942768640e5f655 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 18 Dec 2025 23:16:44 -0800 Subject: [PATCH 14/19] Fix case-sensitive path check in test_get_models_directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was checking for lowercase "canvas" but macOS uses "Canvas" in the path (~/Library/Application Support/ai.baseweight.Canvas/). Made the check case-insensitive. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src-tauri/src/model_manager.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/model_manager.rs b/src-tauri/src/model_manager.rs index c9bce62..aa73914 100644 --- a/src-tauri/src/model_manager.rs +++ b/src-tauri/src/model_manager.rs @@ -493,8 +493,9 @@ mod tests { let result = ModelManager::get_models_directory(); assert!(result.is_ok()); let path = result.unwrap(); - assert!(path.to_string_lossy().contains("canvas")); - assert!(path.to_string_lossy().contains("models")); + let path_lower = path.to_string_lossy().to_lowercase(); + assert!(path_lower.contains("canvas"), "Path should contain 'canvas': {}", path.display()); + assert!(path_lower.contains("models"), "Path should contain 'models': {}", path.display()); } #[tokio::test] From fe6ef6b4235b0509de6bf23bc66d091dcd351e15 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 18 Dec 2025 23:25:35 -0800 Subject: [PATCH 15/19] Fix Windows DLL extraction in CI workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract to named directory and recursively find DLLs since the zip structure may differ from the tar.gz structure on other platforms. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96c5faa..a2b086c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,9 +82,10 @@ jobs: shell: powershell run: | Invoke-WebRequest -Uri "https://github.com/ggml-org/llama.cpp/releases/download/b7423/llama-b7423-bin-win-vulkan-x64.zip" -OutFile "llama-libs.zip" - Expand-Archive -Path "llama-libs.zip" -DestinationPath "." + Expand-Archive -Path "llama-libs.zip" -DestinationPath "llama-extracted" + Get-ChildItem -Recurse llama-extracted | Select-Object FullName New-Item -ItemType Directory -Force -Path src-tauri/libs/windows-x64-vulkan - Copy-Item -Path "llama-b7423/*.dll" -Destination src-tauri/libs/windows-x64-vulkan/ + Get-ChildItem -Path "llama-extracted" -Recurse -Filter "*.dll" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ Get-ChildItem src-tauri/libs/windows-x64-vulkan/ - name: Set Windows backend environment From 1bac786b0ec43556b15f369e3b42557573961bca Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 18 Dec 2025 23:28:20 -0800 Subject: [PATCH 16/19] Fix Windows PATH handling in CI tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use PowerShell to modify PATH within the shell script instead of overriding it in the env block, which was losing the cargo path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/build.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a2b086c..a252f76 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -113,9 +113,10 @@ jobs: - name: Run Rust tests (Windows) if: matrix.platform.os == 'windows-latest' working-directory: src-tauri - env: - PATH: ${{ github.workspace }}\src-tauri\libs\windows-x64-vulkan;${{ env.PATH }} - run: cargo test --verbose + shell: powershell + run: | + $env:PATH = "${{ github.workspace }}\src-tauri\libs\windows-x64-vulkan;$env:PATH" + cargo test --verbose - name: Build frontend run: npm run build From f5ba465c1039fbe46e149b8d1048b9007d0afacd Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 18 Dec 2025 23:41:19 -0800 Subject: [PATCH 17/19] Copy .lib import libraries for Windows linking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows linker needs .lib files to link against DLLs, not just the .dll files themselves. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a252f76..00ffdd0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -86,6 +86,7 @@ jobs: Get-ChildItem -Recurse llama-extracted | Select-Object FullName New-Item -ItemType Directory -Force -Path src-tauri/libs/windows-x64-vulkan Get-ChildItem -Path "llama-extracted" -Recurse -Filter "*.dll" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ + Get-ChildItem -Path "llama-extracted" -Recurse -Filter "*.lib" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ Get-ChildItem src-tauri/libs/windows-x64-vulkan/ - name: Set Windows backend environment From 5d7c0dea4342597e4f17562698245bdb3de337b4 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Fri, 19 Dec 2025 00:09:44 -0800 Subject: [PATCH 18/19] Generate Windows import libraries from DLLs in CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The llama.cpp binary releases only include .dll files, not the .lib import libraries needed for MSVC linking. This adds a step to generate the import libraries using dumpbin and lib.exe from Visual Studio. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/build.yml | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 00ffdd0..5d5b7fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,6 +89,56 @@ jobs: Get-ChildItem -Path "llama-extracted" -Recurse -Filter "*.lib" | Copy-Item -Destination src-tauri/libs/windows-x64-vulkan/ Get-ChildItem src-tauri/libs/windows-x64-vulkan/ + - name: Generate import libraries from DLLs (Windows) + if: matrix.platform.os == 'windows-latest' + shell: powershell + run: | + $libDir = "src-tauri\libs\windows-x64-vulkan" + + # Find Visual Studio installation and setup environment + $vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vsPath = & $vsWhere -latest -property installationPath + $vcvarsall = Join-Path $vsPath "VC\Auxiliary\Build\vcvars64.bat" + + foreach ($name in @("llama", "ggml", "mtmd")) { + $dllPath = Join-Path $libDir "$name.dll" + if (Test-Path $dllPath) { + Write-Host "Generating import library for $name.dll" + + # Run dumpbin and lib in a VS Developer environment + $defFile = Join-Path $libDir "$name.def" + $libFile = Join-Path $libDir "$name.lib" + + # Use cmd to run vcvars and then our commands + cmd /c "`"$vcvarsall`" && dumpbin /exports `"$dllPath`" > `"$libDir\${name}_exports.txt`"" + + # Parse exports and create .def file + $exports = Get-Content "$libDir\${name}_exports.txt" + $inExports = $false + $defContent = "LIBRARY $name`r`nEXPORTS`r`n" + + foreach ($line in $exports) { + if ($line -match "^\s+ordinal\s+hint\s+RVA\s+name") { + $inExports = $true + continue + } + if ($inExports -and $line -match "^\s+\d+\s+[0-9A-Fa-f]+\s+[0-9A-Fa-f]+\s+(\S+)") { + $defContent += " $($Matches[1])`r`n" + } + if ($inExports -and $line -match "^\s*$") { + break + } + } + + Set-Content -Path $defFile -Value $defContent + + # Generate .lib from .def + cmd /c "`"$vcvarsall`" && lib /def:`"$defFile`" /machine:x64 /out:`"$libFile`"" + } + } + + Get-ChildItem "$libDir\*.lib" + - name: Set Windows backend environment if: matrix.platform.os == 'windows-latest' shell: powershell From b4cadde5805acc87feda0a9e91784615c03cb442 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Fri, 19 Dec 2025 07:28:57 -0800 Subject: [PATCH 19/19] Disable Windows CI temporarily MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows builds require import library (.lib) generation from DLLs which needs more work. Disabling for now to unblock Linux/macOS CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d5b7fd..bd5fcfa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,8 +20,9 @@ jobs: rust_target: x86_64-unknown-linux-gnu - os: macos-latest rust_target: aarch64-apple-darwin - - os: windows-latest - rust_target: x86_64-pc-windows-msvc + # Windows disabled temporarily - needs import library generation work + # - os: windows-latest + # rust_target: x86_64-pc-windows-msvc runs-on: ${{ matrix.platform.os }}