refactor: deprecate Vulkan external binaries (#6638)

* refactor: deprecate vulkan binary

refactor: clean up vulkan lib

chore: cleanup

chore: clean up

chore: clean up

fix: build

* fix: skip binaries download env

* Update src-tauri/utils/src/system.rs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src-tauri/utils/src/system.rs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Louis 2025-09-29 17:47:59 +07:00 committed by GitHub
parent 3c4c6d1c50
commit 5fd249c72d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 249 additions and 318 deletions

View File

@ -72,8 +72,7 @@ jobs:
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
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

View File

@ -93,8 +93,7 @@ jobs:
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
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
@ -184,4 +183,3 @@ jobs:
with:
name: jan-linux-amd64-flatpak-${{ inputs.new_version }}-AppImage
path: ./src-tauri/target/release/bundle/appimage/*.AppImage

View File

@ -110,8 +110,7 @@ jobs:
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
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

View File

@ -43,7 +43,6 @@ endif
dev: install-and-build
yarn download:bin
yarn download:lib
yarn dev
# Web application targets
@ -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

View File

@ -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",

View File

@ -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

View File

@ -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)
})

View File

@ -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"

View File

@ -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<R: Runtime>(app: tauri::AppHandle<R>) -> 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<R: Runtime>(app: tauri::AppHandle<R>) -> 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<R: Runtime>(app: tauri::AppHandle<R>) -> SystemInfo {
}
#[tauri::command]
pub fn get_system_usage<R: Runtime>(app: tauri::AppHandle<R>) -> SystemUsage {
pub fn get_system_usage() -> SystemUsage {
let mut system = System::new();
system.refresh_memory();
@ -81,7 +71,7 @@ pub fn get_system_usage<R: Runtime>(app: tauri::AppHandle<R>) -> 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())

View File

@ -1,20 +0,0 @@
use tauri::{path::BaseDirectory, Manager, Runtime};
pub fn get_jan_libvulkan_path<R: Runtime>(app: tauri::AppHandle<R>) -> 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(),
}
}

View File

@ -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;

View File

@ -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);
}
@ -32,10 +30,10 @@ mod cpu_tests {
// 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)
@ -78,11 +76,33 @@ mod cpu_tests {
// 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 {

View File

@ -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);

View File

@ -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<GpuInfo> {
match get_vulkan_gpus_internal(lib_path) {
pub fn get_vulkan_gpus() -> Vec<GpuInfo> {
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<GpuInfo> {
}
}
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<Vec<GpuInfo>, Box<dyn std::error::Error>> {
let library = VulkanLibrary::new()?;
fn get_vulkan_gpus_internal(lib_path: &str) -> Result<Vec<GpuInfo>, Box<dyn std::error::Error>> {
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)
}

View File

@ -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<u64, String> {
}
#[tauri::command]
pub async fn is_model_supported<R: Runtime>(
pub async fn is_model_supported(
path: String,
ctx_size: Option<u32>,
app_handle: tauri::AppHandle<R>,
) -> Result<ModelSupportStatus, String> {
// 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);

View File

@ -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<R: Runtime>(
pub async fn plan_model_load(
path: String,
memory_mode: String,
mmproj_path: Option<String>,
requested_ctx: Option<u64>,
app: tauri::AppHandle<R>,
) -> Result<ModelPlan, String> {
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;

View File

@ -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<R: Runtime>(
}
} 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());

View File

@ -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"
}
}
}

View File

@ -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": {

View File

@ -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 {