diff --git a/.github/workflows/template-tauri-build-linux-x64-external.yml b/.github/workflows/template-tauri-build-linux-x64-external.yml index a88c48267..5c39e17c7 100644 --- a/.github/workflows/template-tauri-build-linux-x64-external.yml +++ b/.github/workflows/template-tauri-build-linux-x64-external.yml @@ -70,10 +70,9 @@ jobs: run: | echo "Version: ${{ inputs.new_version }}" jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = false' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json - mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json + mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json if [ "${{ inputs.channel }}" != "stable" ]; then - jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun", - "usr/lib/Jan-${{ inputs.channel }}/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json + jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json fi jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json @@ -83,7 +82,7 @@ jobs: jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json - + echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------" cat ./src-tauri/plugins/tauri-plugin-hardware/package.json @@ -96,7 +95,7 @@ jobs: ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}" echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------" cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml - + ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}" echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------" cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml @@ -125,7 +124,7 @@ jobs: env: RELEASE_CHANNEL: '${{ inputs.channel }}' AUTO_UPDATER_DISABLED: ${{ inputs.disable_updater && 'true' || 'false' }} - + - name: Upload Artifact uses: actions/upload-artifact@v4 with: @@ -136,4 +135,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: jan-linux-amd64-${{ inputs.new_version }}-AppImage - path: ./src-tauri/target/release/bundle/appimage/*.AppImage \ No newline at end of file + path: ./src-tauri/target/release/bundle/appimage/*.AppImage diff --git a/.github/workflows/template-tauri-build-linux-x64-flatpak.yml b/.github/workflows/template-tauri-build-linux-x64-flatpak.yml index 15d4827f7..d63fae3e7 100644 --- a/.github/workflows/template-tauri-build-linux-x64-flatpak.yml +++ b/.github/workflows/template-tauri-build-linux-x64-flatpak.yml @@ -91,10 +91,9 @@ jobs: echo "Version: ${{ inputs.new_version }}" # Update tauri.conf.json jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = true' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json - mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json + mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json if [ "${{ inputs.channel }}" != "stable" ]; then - jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun", - "usr/lib/Jan-${{ inputs.channel }}/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json + jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json fi jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json @@ -104,7 +103,7 @@ jobs: jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json - + echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------" cat ./src-tauri/plugins/tauri-plugin-hardware/package.json @@ -117,7 +116,7 @@ jobs: ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}" echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------" cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml - + ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}" echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------" cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml @@ -128,7 +127,7 @@ jobs: # Temporarily enable devtool on prod build ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools" - cat ./src-tauri/Cargo.toml + cat ./src-tauri/Cargo.toml # Change app name for beta and nightly builds if [ "${{ inputs.channel }}" != "stable" ]; then @@ -139,7 +138,7 @@ jobs: .github/scripts/rename-tauri-app.sh ./src-tauri/tauri.conf.json ${{ inputs.channel }} cat ./src-tauri/tauri.conf.json - + # Update Cargo.toml ctoml ./src-tauri/Cargo.toml package.name "Jan-${{ inputs.channel }}" ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools" @@ -184,4 +183,3 @@ jobs: with: name: jan-linux-amd64-flatpak-${{ inputs.new_version }}-AppImage path: ./src-tauri/target/release/bundle/appimage/*.AppImage - diff --git a/.github/workflows/template-tauri-build-linux-x64.yml b/.github/workflows/template-tauri-build-linux-x64.yml index bd9b38369..487571595 100644 --- a/.github/workflows/template-tauri-build-linux-x64.yml +++ b/.github/workflows/template-tauri-build-linux-x64.yml @@ -108,10 +108,9 @@ jobs: echo "Version: ${{ inputs.new_version }}" # Update tauri.conf.json jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = true' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json - mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json + mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json if [ "${{ inputs.channel }}" != "stable" ]; then - jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun", - "usr/lib/Jan-${{ inputs.channel }}/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json + jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json fi jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json @@ -121,7 +120,7 @@ jobs: jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json - + echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------" cat ./src-tauri/plugins/tauri-plugin-hardware/package.json @@ -134,7 +133,7 @@ jobs: ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}" echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------" cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml - + ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}" echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------" cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml @@ -156,7 +155,7 @@ jobs: .github/scripts/rename-tauri-app.sh ./src-tauri/tauri.conf.json ${{ inputs.channel }} cat ./src-tauri/tauri.conf.json - + # Update Cargo.toml ctoml ./src-tauri/Cargo.toml package.name "Jan-${{ inputs.channel }}" ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools" diff --git a/Makefile b/Makefile index 9a03ddaad..e2262a842 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,6 @@ endif dev: install-and-build yarn download:bin - yarn download:lib yarn dev # Web application targets @@ -58,7 +57,7 @@ build-web-app: install-web-app yarn build:core yarn build:web-app -serve-web-app: +serve-web-app: yarn serve:web-app build-serve-web-app: build-web-app @@ -71,7 +70,6 @@ lint: install-and-build # Testing test: lint yarn download:bin - yarn download:lib ifeq ($(OS),Windows_NT) yarn download:windows-installer endif diff --git a/package.json b/package.json index 50eb8ecaf..fe5fdff21 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,10 @@ "build:serve:web-app": "yarn build:web-app && yarn serve:web-app", "dev:tauri": "yarn build:icon && yarn copy:assets:tauri && cross-env IS_CLEAN=true tauri dev", "copy:assets:tauri": "cpx \"pre-install/*.tgz\" \"src-tauri/resources/pre-install/\" && cpx \"LICENSE\" \"src-tauri/resources/\"", - "download:lib": "node ./scripts/download-lib.mjs", "download:bin": "node ./scripts/download-bin.mjs", "download:windows-installer": "node ./scripts/download-win-installer-deps.mjs", - "build:tauri:win32": "yarn download:bin && yarn download:lib && yarn download:windows-installer && yarn tauri build", - "build:tauri:linux": "yarn download:bin && yarn download:lib && NO_STRIP=1 ./src-tauri/build-utils/shim-linuxdeploy.sh yarn tauri build && ./src-tauri/build-utils/buildAppImage.sh", + "build:tauri:win32": "yarn download:bin && yarn download:windows-installer && yarn tauri build", + "build:tauri:linux": "yarn download:bin && NO_STRIP=1 ./src-tauri/build-utils/shim-linuxdeploy.sh yarn tauri build && ./src-tauri/build-utils/buildAppImage.sh", "build:tauri:darwin": "yarn download:bin && yarn tauri build --target universal-apple-darwin", "build:tauri": "yarn build:icon && yarn copy:assets:tauri && run-script-os", "build:tauri:plugin:api": "cd src-tauri/plugins && yarn install && yarn workspaces foreach -Apt run build", diff --git a/scripts/download-bin.mjs b/scripts/download-bin.mjs index 36e17b3f0..39eb9ae87 100644 --- a/scripts/download-bin.mjs +++ b/scripts/download-bin.mjs @@ -1,4 +1,3 @@ -console.log('Script is running') // scripts/download.js import https from 'https' import fs, { copyFile, mkdirSync } from 'fs' @@ -69,7 +68,10 @@ function getPlatformArch() { arch === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin' } else if (platform === 'linux') { bunPlatform = arch === 'arm64' ? 'linux-aarch64' : 'linux-x64' - uvPlatform = arch === 'arm64' ? 'aarch64-unknown-linux-gnu' : 'x86_64-unknown-linux-gnu' + uvPlatform = + arch === 'arm64' + ? 'aarch64-unknown-linux-gnu' + : 'x86_64-unknown-linux-gnu' } else if (platform === 'win32') { bunPlatform = 'windows-x64' // Bun has limited Windows support uvPlatform = 'x86_64-pc-windows-msvc' @@ -81,6 +83,10 @@ function getPlatformArch() { } async function main() { + if (process.env.SKIP_BINARIES) { + console.log('Skipping binaries download.') + process.exit(0) + } console.log('Starting main function') const platform = os.platform() const { bunPlatform, uvPlatform } = getPlatformArch() @@ -124,29 +130,45 @@ async function main() { if (err) { console.log('Add execution permission failed!', err) } - }); + }) if (platform === 'darwin') { - copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-x86_64-apple-darwin'), (err) => { - if (err) { - console.log("Error Found:", err); - } - }) - copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-aarch64-apple-darwin'), (err) => { - if (err) { - console.log("Error Found:", err); - } - }) - copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-universal-apple-darwin'), (err) => { + copyFile( + path.join(binDir, 'bun'), + path.join(binDir, 'bun-x86_64-apple-darwin'), + (err) => { if (err) { - console.log("Error Found:", err); + console.log('Error Found:', err) } - }) - } else if (platform === 'linux') { - copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-x86_64-unknown-linux-gnu'), (err) => { - if (err) { - console.log("Error Found:", err); } - }) + ) + copyFile( + path.join(binDir, 'bun'), + path.join(binDir, 'bun-aarch64-apple-darwin'), + (err) => { + if (err) { + console.log('Error Found:', err) + } + } + ) + copyFile( + path.join(binDir, 'bun'), + path.join(binDir, 'bun-universal-apple-darwin'), + (err) => { + if (err) { + console.log('Error Found:', err) + } + } + ) + } else if (platform === 'linux') { + copyFile( + path.join(binDir, 'bun'), + path.join(binDir, 'bun-x86_64-unknown-linux-gnu'), + (err) => { + if (err) { + console.log('Error Found:', err) + } + } + ) } } catch (err) { // Expect EEXIST error @@ -157,11 +179,15 @@ async function main() { path.join(binDir) ) if (platform === 'win32') { - copyFile(path.join(binDir, 'bun.exe'), path.join(binDir, 'bun-x86_64-pc-windows-msvc.exe'), (err) => { - if (err) { - console.log("Error Found:", err); + copyFile( + path.join(binDir, 'bun.exe'), + path.join(binDir, 'bun-x86_64-pc-windows-msvc.exe'), + (err) => { + if (err) { + console.log('Error Found:', err) + } } - }) + ) } } catch (err) { // Expect EEXIST error @@ -176,52 +202,66 @@ async function main() { await decompress(uvPath, tempBinDir) } try { - copySync( - path.join(tempBinDir, `uv-${uvPlatform}`, 'uv'), - path.join(binDir) - ) + copySync(path.join(tempBinDir, `uv-${uvPlatform}`, 'uv'), path.join(binDir)) fs.chmod(path.join(binDir, 'uv'), 0o755, (err) => { if (err) { console.log('Add execution permission failed!', err) } - }); + }) if (platform === 'darwin') { - copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-x86_64-apple-darwin'), (err) => { - if (err) { - console.log("Error Found:", err); + copyFile( + path.join(binDir, 'uv'), + path.join(binDir, 'uv-x86_64-apple-darwin'), + (err) => { + if (err) { + console.log('Error Found:', err) + } } - }) - copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-aarch64-apple-darwin'), (err) => { - if (err) { - console.log("Error Found:", err); + ) + copyFile( + path.join(binDir, 'uv'), + path.join(binDir, 'uv-aarch64-apple-darwin'), + (err) => { + if (err) { + console.log('Error Found:', err) + } } - }) - copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-universal-apple-darwin'), (err) => { - if (err) { - console.log("Error Found:", err); + ) + copyFile( + path.join(binDir, 'uv'), + path.join(binDir, 'uv-universal-apple-darwin'), + (err) => { + if (err) { + console.log('Error Found:', err) + } } - }) + ) } else if (platform === 'linux') { - copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-x86_64-unknown-linux-gnu'), (err) => { - if (err) { - console.log("Error Found:", err); + copyFile( + path.join(binDir, 'uv'), + path.join(binDir, 'uv-x86_64-unknown-linux-gnu'), + (err) => { + if (err) { + console.log('Error Found:', err) + } } - }) + ) } } catch (err) { // Expect EEXIST error } try { - copySync( - path.join(tempBinDir, 'uv.exe'), - path.join(binDir) - ) + copySync(path.join(tempBinDir, 'uv.exe'), path.join(binDir)) if (platform === 'win32') { - copyFile(path.join(binDir, 'uv.exe'), path.join(binDir, 'uv-x86_64-pc-windows-msvc.exe'), (err) => { - if (err) { - console.log("Error Found:", err); + copyFile( + path.join(binDir, 'uv.exe'), + path.join(binDir, 'uv-x86_64-pc-windows-msvc.exe'), + (err) => { + if (err) { + console.log('Error Found:', err) + } } - }) + ) } } catch (err) { // Expect EEXIST error diff --git a/scripts/download-lib.mjs b/scripts/download-lib.mjs deleted file mode 100644 index d2086b36e..000000000 --- a/scripts/download-lib.mjs +++ /dev/null @@ -1,86 +0,0 @@ -console.log('Script is running') -// scripts/download-lib.mjs -import https from 'https' -import fs, { mkdirSync } from 'fs' -import os from 'os' -import path from 'path' -import { copySync } from 'cpx' - -function download(url, dest) { - return new Promise((resolve, reject) => { - console.log(`Downloading ${url} to ${dest}`) - const file = fs.createWriteStream(dest) - https - .get(url, (response) => { - console.log(`Response status code: ${response.statusCode}`) - if ( - response.statusCode >= 300 && - response.statusCode < 400 && - response.headers.location - ) { - // Handle redirect - const redirectURL = response.headers.location - console.log(`Redirecting to ${redirectURL}`) - download(redirectURL, dest).then(resolve, reject) // Recursive call - return - } else if (response.statusCode !== 200) { - reject(`Failed to get '${url}' (${response.statusCode})`) - return - } - response.pipe(file) - file.on('finish', () => { - file.close(resolve) - }) - }) - .on('error', (err) => { - fs.unlink(dest, () => reject(err.message)) - }) - }) -} - -async function main() { - console.log('Starting main function') - const platform = os.platform() // 'darwin', 'linux', 'win32' - const arch = os.arch() // 'x64', 'arm64', etc. - - if (arch != 'x64') return - - let filename - if (platform == 'linux') - filename = 'libvulkan.so' - else if (platform == 'win32') - filename = 'vulkan-1.dll' - else - return - - const url = `https://catalog.jan.ai/${filename}` - - const libDir = 'src-tauri/resources/lib' - const tempDir = 'scripts/dist' - - try { - mkdirSync('scripts/dist') - } catch (err) { - // Expect EEXIST error if the directory already exists - } - - console.log(`Downloading libvulkan...`) - const savePath = path.join(tempDir, filename) - if (!fs.existsSync(savePath)) { - await download(url, savePath) - } - - // copy to tauri resources - try { - copySync(savePath, libDir) - } catch (err) { - // Expect EEXIST error - } - - console.log('Downloads completed.') -} - -main().catch((err) => { - console.error('Error:', err) - process.exit(1) -}) diff --git a/src-tauri/plugins/tauri-plugin-hardware/Cargo.toml b/src-tauri/plugins/tauri-plugin-hardware/Cargo.toml index eb74d32d1..7475fb353 100644 --- a/src-tauri/plugins/tauri-plugin-hardware/Cargo.toml +++ b/src-tauri/plugins/tauri-plugin-hardware/Cargo.toml @@ -11,7 +11,7 @@ exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"] links = "tauri-plugin-hardware" [dependencies] -ash = "0.38.0" +vulkano = "0.34" libc = "0.2" log = "0.4" nvml-wrapper = "0.10.0" diff --git a/src-tauri/plugins/tauri-plugin-hardware/src/commands.rs b/src-tauri/plugins/tauri-plugin-hardware/src/commands.rs index 56e78f1c1..ac13eb7f2 100644 --- a/src-tauri/plugins/tauri-plugin-hardware/src/commands.rs +++ b/src-tauri/plugins/tauri-plugin-hardware/src/commands.rs @@ -1,14 +1,12 @@ use crate::{ - helpers::get_jan_libvulkan_path, types::{CpuStaticInfo, SystemInfo, SystemUsage}, vendor::{nvidia, vulkan}, SYSTEM_INFO, }; use sysinfo::System; -use tauri::Runtime; #[tauri::command] -pub fn get_system_info(app: tauri::AppHandle) -> SystemInfo { +pub fn get_system_info() -> SystemInfo { SYSTEM_INFO .get_or_init(|| { let mut system = System::new(); @@ -19,15 +17,7 @@ pub fn get_system_info(app: tauri::AppHandle) -> SystemInfo { gpu_map.insert(gpu.uuid.clone(), gpu); } - // try system vulkan first - let paths = vec!["".to_string(), get_jan_libvulkan_path(app.clone())]; - let mut vulkan_gpus = vec![]; - for path in paths { - vulkan_gpus = vulkan::get_vulkan_gpus(&path); - if !vulkan_gpus.is_empty() { - break; - } - } + let vulkan_gpus = vulkan::get_vulkan_gpus(); for gpu in vulkan_gpus { match gpu_map.get_mut(&gpu.uuid) { @@ -64,7 +54,7 @@ pub fn get_system_info(app: tauri::AppHandle) -> SystemInfo { } #[tauri::command] -pub fn get_system_usage(app: tauri::AppHandle) -> SystemUsage { +pub fn get_system_usage() -> SystemUsage { let mut system = System::new(); system.refresh_memory(); @@ -81,7 +71,7 @@ pub fn get_system_usage(app: tauri::AppHandle) -> SystemUsage { cpu: cpu_usage, used_memory: system.used_memory() / 1024 / 1024, // bytes to MiB, total_memory: system.total_memory() / 1024 / 1024, // bytes to MiB, - gpus: get_system_info(app.clone()) + gpus: get_system_info() .gpus .iter() .map(|gpu| gpu.get_usage()) diff --git a/src-tauri/plugins/tauri-plugin-hardware/src/helpers.rs b/src-tauri/plugins/tauri-plugin-hardware/src/helpers.rs deleted file mode 100644 index 22bcc8669..000000000 --- a/src-tauri/plugins/tauri-plugin-hardware/src/helpers.rs +++ /dev/null @@ -1,20 +0,0 @@ -use tauri::{path::BaseDirectory, Manager, Runtime}; - -pub fn get_jan_libvulkan_path(app: tauri::AppHandle) -> String { - let lib_name = if cfg!(target_os = "windows") { - "vulkan-1.dll" - } else if cfg!(target_os = "linux") { - "libvulkan.so" - } else { - return "".to_string(); - }; - - // NOTE: this does not work in test mode (mock app) - match app.path().resolve( - format!("resources/lib/{}", lib_name), - BaseDirectory::Resource, - ) { - Ok(lib_path) => lib_path.to_string_lossy().to_string(), - Err(_) => "".to_string(), - } -} diff --git a/src-tauri/plugins/tauri-plugin-hardware/src/lib.rs b/src-tauri/plugins/tauri-plugin-hardware/src/lib.rs index 228a3731e..3a069892e 100644 --- a/src-tauri/plugins/tauri-plugin-hardware/src/lib.rs +++ b/src-tauri/plugins/tauri-plugin-hardware/src/lib.rs @@ -2,12 +2,10 @@ mod commands; mod constants; pub mod cpu; pub mod gpu; -mod helpers; mod types; pub mod vendor; pub use constants::*; -pub use helpers::*; pub use types::*; use std::sync::OnceLock; diff --git a/src-tauri/plugins/tauri-plugin-hardware/src/tests.rs b/src-tauri/plugins/tauri-plugin-hardware/src/tests.rs index 1d4975104..f27554579 100644 --- a/src-tauri/plugins/tauri-plugin-hardware/src/tests.rs +++ b/src-tauri/plugins/tauri-plugin-hardware/src/tests.rs @@ -4,15 +4,13 @@ use tauri::test::mock_app; #[test] fn test_system_info() { - let app = mock_app(); - let info = get_system_info(app.handle().clone()); + let info = get_system_info(); println!("System Static Info: {:?}", info); } #[test] fn test_system_usage() { - let app = mock_app(); - let usage = get_system_usage(app.handle().clone()); + let usage = get_system_usage(); println!("System Usage Info: {:?}", usage); } @@ -23,23 +21,23 @@ mod cpu_tests { #[test] fn test_cpu_static_info_new() { let cpu_info = CpuStaticInfo::new(); - + // Test that all fields are populated assert!(!cpu_info.name.is_empty()); assert_ne!(cpu_info.name, "unknown"); // Should have detected a CPU name assert!(cpu_info.core_count > 0); assert!(!cpu_info.arch.is_empty()); - + // Architecture should be one of the expected values assert!( - cpu_info.arch == "aarch64" || - cpu_info.arch == "arm64" || - cpu_info.arch == "x86_64" || - cpu_info.arch == std::env::consts::ARCH + cpu_info.arch == "aarch64" + || cpu_info.arch == "arm64" + || cpu_info.arch == "x86_64" + || cpu_info.arch == std::env::consts::ARCH ); - + // Extensions should be a valid list (can be empty on non-x86) - + println!("CPU Info: {:?}", cpu_info); } @@ -48,7 +46,7 @@ mod cpu_tests { // Test that multiple calls return consistent information let info1 = CpuStaticInfo::new(); let info2 = CpuStaticInfo::new(); - + assert_eq!(info1.name, info2.name); assert_eq!(info1.core_count, info2.core_count); assert_eq!(info1.arch, info2.arch); @@ -72,19 +70,41 @@ mod cpu_tests { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn test_x86_extensions() { let cpu_info = CpuStaticInfo::new(); - + // On x86/x86_64, we should always have at least FPU assert!(cpu_info.extensions.contains(&"fpu".to_string())); - + // Check that all extensions are valid x86 feature names let valid_extensions = [ - "fpu", "mmx", "sse", "sse2", "sse3", "ssse3", "sse4_1", "sse4_2", - "pclmulqdq", "avx", "avx2", "avx512_f", "avx512_dq", "avx512_ifma", - "avx512_pf", "avx512_er", "avx512_cd", "avx512_bw", "avx512_vl", - "avx512_vbmi", "avx512_vbmi2", "avx512_vnni", "avx512_bitalg", - "avx512_vpopcntdq", "avx512_vp2intersect", "aes", "f16c" + "fpu", + "mmx", + "sse", + "sse2", + "sse3", + "ssse3", + "sse4_1", + "sse4_2", + "pclmulqdq", + "avx", + "avx2", + "avx512_f", + "avx512_dq", + "avx512_ifma", + "avx512_pf", + "avx512_er", + "avx512_cd", + "avx512_bw", + "avx512_vl", + "avx512_vbmi", + "avx512_vbmi2", + "avx512_vnni", + "avx512_bitalg", + "avx512_vpopcntdq", + "avx512_vp2intersect", + "aes", + "f16c", ]; - + for ext in &cpu_info.extensions { assert!( valid_extensions.contains(&ext.as_str()), @@ -98,7 +118,7 @@ mod cpu_tests { #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] fn test_non_x86_extensions() { let cpu_info = CpuStaticInfo::new(); - + // On non-x86 architectures, extensions should be empty assert!(cpu_info.extensions.is_empty()); } @@ -106,15 +126,15 @@ mod cpu_tests { #[test] fn test_arch_detection() { let cpu_info = CpuStaticInfo::new(); - + // Architecture should be a valid string assert!(!cpu_info.arch.is_empty()); - + // Should be one of the common architectures let common_archs = ["x86_64", "aarch64", "arm", "arm64", "x86"]; let is_common_arch = common_archs.iter().any(|&arch| cpu_info.arch == arch); let is_compile_time_arch = cpu_info.arch == std::env::consts::ARCH; - + assert!( is_common_arch || is_compile_time_arch, "Unexpected architecture: {}", @@ -125,11 +145,11 @@ mod cpu_tests { #[test] fn test_cpu_info_serialization() { let cpu_info = CpuStaticInfo::new(); - + // Test that the struct can be serialized (since it derives Serialize) let serialized = serde_json::to_string(&cpu_info); assert!(serialized.is_ok()); - + let json_str = serialized.unwrap(); assert!(json_str.contains("name")); assert!(json_str.contains("core_count")); diff --git a/src-tauri/plugins/tauri-plugin-hardware/src/vendor/tests.rs b/src-tauri/plugins/tauri-plugin-hardware/src/vendor/tests.rs index 078efe91b..ad48a6fad 100644 --- a/src-tauri/plugins/tauri-plugin-hardware/src/vendor/tests.rs +++ b/src-tauri/plugins/tauri-plugin-hardware/src/vendor/tests.rs @@ -12,7 +12,7 @@ fn test_get_nvidia_gpus() { #[test] fn test_get_vulkan_gpus() { - let gpus = vulkan::get_vulkan_gpus(""); + let gpus = vulkan::get_vulkan_gpus(); for (i, gpu) in gpus.iter().enumerate() { println!("GPU {}:", i); println!(" {:?}", gpu); diff --git a/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs b/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs index 6a9bf21aa..91af52e5f 100644 --- a/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs +++ b/src-tauri/plugins/tauri-plugin-hardware/src/vendor/vulkan.rs @@ -1,5 +1,8 @@ use crate::types::{GpuInfo, Vendor}; -use ash::{vk, Entry}; +use vulkano::device::physical::PhysicalDeviceType; +use vulkano::instance::{Instance, InstanceCreateInfo}; +use vulkano::memory::MemoryHeapFlags; +use vulkano::VulkanLibrary; #[derive(Debug, Clone, serde::Serialize)] pub struct VulkanInfo { @@ -35,8 +38,8 @@ fn parse_uuid(bytes: &[u8; 16]) -> String { ) } -pub fn get_vulkan_gpus(lib_path: &str) -> Vec { - match get_vulkan_gpus_internal(lib_path) { +pub fn get_vulkan_gpus() -> Vec { + match get_vulkan_gpus_internal() { Ok(gpus) => gpus, Err(e) => { log::error!("Failed to get Vulkan GPUs: {:?}", e); @@ -45,86 +48,59 @@ pub fn get_vulkan_gpus(lib_path: &str) -> Vec { } } -fn parse_c_string(buf: &[i8]) -> String { - unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) } - .to_str() - .unwrap_or_default() - .to_string() -} +fn get_vulkan_gpus_internal() -> Result, Box> { + let library = VulkanLibrary::new()?; -fn get_vulkan_gpus_internal(lib_path: &str) -> Result, Box> { - let entry = if lib_path.is_empty() { - unsafe { Entry::load()? } - } else { - unsafe { Entry::load_from(lib_path)? } - }; - let app_info = vk::ApplicationInfo { - api_version: vk::make_api_version(0, 1, 1, 0), - ..Default::default() - }; - let create_info = vk::InstanceCreateInfo { - p_application_info: &app_info, - ..Default::default() - }; - let instance = unsafe { entry.create_instance(&create_info, None)? }; + let instance = Instance::new( + library, + InstanceCreateInfo { + application_name: Some("Jan GPU Detection".into()), + application_version: vulkano::Version::V1_1, + ..Default::default() + }, + )?; let mut device_info_list = vec![]; - for (i, device) in unsafe { instance.enumerate_physical_devices()? } - .iter() - .enumerate() - { - // create a chain of properties struct for VkPhysicalDeviceProperties2(3) - // https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceProperties2.html - // props2 -> driver_props -> id_props - let mut id_props = vk::PhysicalDeviceIDProperties::default(); - let mut driver_props = vk::PhysicalDeviceDriverProperties { - p_next: &mut id_props as *mut _ as *mut std::ffi::c_void, - ..Default::default() - }; - let mut props2 = vk::PhysicalDeviceProperties2 { - p_next: &mut driver_props as *mut _ as *mut std::ffi::c_void, - ..Default::default() - }; - unsafe { - instance.get_physical_device_properties2(*device, &mut props2); - } + for (i, physical_device) in instance.enumerate_physical_devices()?.enumerate() { + let properties = physical_device.properties(); - let props = props2.properties; - if props.device_type == vk::PhysicalDeviceType::CPU { + if properties.device_type == PhysicalDeviceType::Cpu { continue; } + let memory_properties = physical_device.memory_properties(); + let total_memory: u64 = memory_properties + .memory_heaps + .iter() + .filter(|heap| heap.flags.intersects(MemoryHeapFlags::DEVICE_LOCAL)) + .map(|heap| heap.size / (1024 * 1024)) + .sum(); + + let device_uuid = physical_device.properties().device_uuid.unwrap_or([0; 16]); + let driver_version = format!("{}", properties.driver_version); + let device_info = GpuInfo { - name: parse_c_string(&props.device_name), - total_memory: unsafe { instance.get_physical_device_memory_properties(*device) } - .memory_heaps - .iter() - .filter(|heap| heap.flags.contains(vk::MemoryHeapFlags::DEVICE_LOCAL)) - .map(|heap| heap.size / (1024 * 1024)) - .sum(), - vendor: Vendor::from_vendor_id(props.vendor_id), - uuid: parse_uuid(&id_props.device_uuid), - driver_version: parse_c_string(&driver_props.driver_info), + name: properties.device_name.clone(), + total_memory, + vendor: Vendor::from_vendor_id(properties.vendor_id), + uuid: parse_uuid(&device_uuid), + driver_version, nvidia_info: None, vulkan_info: Some(VulkanInfo { index: i as u64, - device_type: format!("{:?}", props.device_type), + device_type: format!("{:?}", properties.device_type), api_version: format!( "{}.{}.{}", - vk::api_version_major(props.api_version), - vk::api_version_minor(props.api_version), - vk::api_version_patch(props.api_version) + properties.api_version.major, + properties.api_version.minor, + properties.api_version.patch ), - device_id: props.device_id, + device_id: properties.device_id, }), }; device_info_list.push(device_info); } - unsafe { - instance.destroy_instance(None); - } - Ok(device_info_list) } diff --git a/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/commands.rs b/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/commands.rs index c636fa8bd..03e949eba 100644 --- a/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/commands.rs +++ b/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/commands.rs @@ -3,7 +3,6 @@ use super::utils::{estimate_kv_cache_internal, read_gguf_metadata_internal}; use crate::gguf::types::{KVCacheError, KVCacheEstimate, ModelSupportStatus}; use std::collections::HashMap; use std::fs; -use tauri::Runtime; use tauri_plugin_hardware::get_system_info; /// Read GGUF metadata from a model file #[tauri::command] @@ -49,16 +48,15 @@ pub async fn get_model_size(path: String) -> Result { } #[tauri::command] -pub async fn is_model_supported( +pub async fn is_model_supported( path: String, ctx_size: Option, - app_handle: tauri::AppHandle, ) -> Result { // Get model size let model_size = get_model_size(path.clone()).await?; // Get system info - let system_info = get_system_info(app_handle.clone()); + let system_info = get_system_info(); log::info!("modelSize: {}", model_size); diff --git a/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/model_planner.rs b/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/model_planner.rs index 118894871..14642af60 100644 --- a/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/model_planner.rs +++ b/src-tauri/plugins/tauri-plugin-llamacpp/src/gguf/model_planner.rs @@ -3,7 +3,6 @@ use crate::gguf::utils::estimate_kv_cache_internal; use crate::gguf::utils::read_gguf_metadata_internal; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use tauri::Runtime; use tauri_plugin_hardware::get_system_info; #[derive(Serialize, Deserialize, Clone, Debug)] @@ -27,15 +26,14 @@ pub enum ModelMode { } #[tauri::command] -pub async fn plan_model_load( +pub async fn plan_model_load( path: String, memory_mode: String, mmproj_path: Option, requested_ctx: Option, - app: tauri::AppHandle, ) -> Result { let model_size = get_model_size(path.clone()).await?; - let sys_info = get_system_info(app.clone()); + let sys_info = get_system_info(); let gguf = read_gguf_metadata_internal(path.clone()).await?; let mut mmproj_size: u64 = 0; diff --git a/src-tauri/src/core/mcp/helpers.rs b/src-tauri/src/core/mcp/helpers.rs index 80a8b5f86..7f8565e39 100644 --- a/src-tauri/src/core/mcp/helpers.rs +++ b/src-tauri/src/core/mcp/helpers.rs @@ -25,7 +25,7 @@ use crate::core::{ mcp::models::McpServerConfig, state::{AppState, RunningServiceEnum, SharedMcpServers}, }; -use jan_utils::can_override_npx; +use jan_utils::{can_override_npx, can_override_uvx}; /// Calculate exponential backoff delay with jitter /// @@ -627,19 +627,20 @@ async fn schedule_mcp_start_task( } } else { let mut cmd = Command::new(config_params.command.clone()); - if config_params.command.clone() == "npx" && can_override_npx() { + let bun_x_path = format!("{}/bun", bin_path.display()); + if config_params.command.clone() == "npx" && can_override_npx(bun_x_path.clone()) { let mut cache_dir = app_path.clone(); cache_dir.push(".npx"); - let bun_x_path = format!("{}/bun", bin_path.display()); cmd = Command::new(bun_x_path); cmd.arg("x"); cmd.env("BUN_INSTALL", cache_dir.to_str().unwrap().to_string()); } - if config_params.command.clone() == "uvx" { + + let uv_path = format!("{}/uv", bin_path.display()); + if config_params.command.clone() == "uvx" && can_override_uvx(uv_path.clone()) { let mut cache_dir = app_path.clone(); cache_dir.push(".uvx"); - let bun_x_path = format!("{}/uv", bin_path.display()); - cmd = Command::new(bun_x_path); + cmd = Command::new(uv_path); cmd.arg("tool"); cmd.arg("run"); cmd.env("UV_CACHE_DIR", cache_dir.to_str().unwrap().to_string()); diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json index 80e7446ff..6b0684b25 100644 --- a/src-tauri/tauri.linux.conf.json +++ b/src-tauri/tauri.linux.conf.json @@ -6,13 +6,11 @@ "linux": { "appimage": { "bundleMediaFramework": false, - "files": { - } + "files": {} }, "deb": { "files": { - "usr/bin/bun": "resources/bin/bun", - "usr/lib/Jan/resources/lib/libvulkan.so": "resources/lib/libvulkan.so" + "usr/bin/bun": "resources/bin/bun" } } } diff --git a/src-tauri/tauri.windows.conf.json b/src-tauri/tauri.windows.conf.json index 16cb9b10a..dad811467 100644 --- a/src-tauri/tauri.windows.conf.json +++ b/src-tauri/tauri.windows.conf.json @@ -1,7 +1,11 @@ { "bundle": { "targets": ["nsis"], - "resources": ["resources/pre-install/**/*", "resources/lib/vulkan-1.dll", "resources/lib/vc_redist.x64.exe", "resources/LICENSE"], + "resources": [ + "resources/pre-install/**/*", + "resources/lib/vc_redist.x64.exe", + "resources/LICENSE" + ], "externalBin": ["resources/bin/bun", "resources/bin/uv"], "windows": { "nsis": { diff --git a/src-tauri/utils/src/system.rs b/src-tauri/utils/src/system.rs index d4ebc79af..cf281b3cb 100644 --- a/src-tauri/utils/src/system.rs +++ b/src-tauri/utils/src/system.rs @@ -1,5 +1,5 @@ -/// Checks AVX2 CPU support for npx override with bun binary -pub fn can_override_npx() -> bool { +/// Checks if npx can be overridden with bun binary +pub fn can_override_npx(bun_path: String) -> bool { // We need to check the CPU for the AVX2 instruction support if we are running under MacOS // with Intel CPU. We can override `npx` command with `bun` only if CPU is // supporting AVX2, otherwise we need to use default `npx` binary @@ -13,10 +13,31 @@ pub fn can_override_npx() -> bool { return false; // we cannot override npx with bun binary } } - + // Check if bun_path exists + if !std::path::Path::new(bun_path.as_str()).exists() { + #[cfg(feature = "logging")] + log::warn!( + "bun binary not found at '{}', default npx binary will be used", + bun_path + ); + return false; + } true // by default, we can override npx with bun binary } +/// Checks if uv_path exists and determines if uvx can be overridden with the uv binary +pub fn can_override_uvx(uv_path: String) -> bool { + if !std::path::Path::new(uv_path.as_str()).exists() { + #[cfg(feature = "logging")] + log::warn!( + "uv binary not found at '{}', default uvx binary will be used", + uv_path + ); + return false; + } + true // by default, we can override uvx with uv binary +} + /// Setup library paths for different operating systems pub fn setup_library_path(library_path: Option<&str>, command: &mut tokio::process::Command) { if let Some(lib_path) = library_path {