diff --git a/src/backend/src/lib.rs b/src/backend/src/lib.rs index 14e50fe..b304b53 100644 --- a/src/backend/src/lib.rs +++ b/src/backend/src/lib.rs @@ -159,98 +159,70 @@ "vec0.dll".to_string() } -fn spawn_llama_server(app_handle: &tauri::AppHandle, config: &serde_json::Value) { - let model_path = if let Ok(p) = env::var("LLAMA_CPP_MODEL_PATH") { PathBuf::from(p) } else if let Some(p) = config.get("model").and_then(|m| m.get("path")).and_then(|p| p.as_str()) { - let mut candidate = PathBuf::from(p); - if candidate.is_relative() { - if cfg!(debug_assertions) { - if let Ok(exe_path) = env::current_exe() { - if let Some(exe_dir) = exe_path.parent() { - let mut pr = exe_dir.to_path_buf(); - for _ in 0..4 { - if pr.join("config.json").exists() { candidate = pr.join(p); break; } - if !pr.pop() { break; } - } - } - } - } else { - if let Ok(res_dir) = app_handle.path().resource_dir() { - candidate = res_dir.join(p); - } - } +fn spawn_llama_server(app_handle: &tauri::AppHandle, _config: &serde_json::Value) { + use tauri_plugin_shell::ShellExt; + + // Use Tauri 2's sidecar API instead of manual Command spawning. + // This automatically handles executable naming (adding -x86_64-pc-windows-msvc etc.) + // and environment variables for DLL discovery in the sidecar folder. + let sidecar = match app_handle.shell().sidecar("llama-server") { + Ok(s) => s, + Err(e) => { + eprintln!("CRITICAL: Failed to create sidecar handle: {}", e); + return; } - candidate - } else { - let mut found = None; - if cfg!(debug_assertions) { - if let Ok(exe_path) = env::current_exe() { - if let Some(exe_dir) = exe_path.parent() { - let mut pr = exe_dir.to_path_buf(); - for _ in 0..4 { - if pr.join("models").exists() { found = Some(pr.join("models").join("embeddinggemma-300m-q4_0.gguf")); break; } - if !pr.pop() { break; } - } - } - } - } else { - if let Ok(res_dir) = app_handle.path().resource_dir() { - found = Some(res_dir.join("models").join("embeddinggemma-300m-q4_0.gguf")); - } - } - found.unwrap_or_else(|| PathBuf::from("models/embeddinggemma-300m-q4_0.gguf")) }; - let sidecar_exe = if cfg!(debug_assertions) { - let mut p = env::current_dir().unwrap(); - if p.ends_with(format!("src{}backend", std::path::MAIN_SEPARATOR)) { p.pop(); p.pop(); } - p.join("bin").join("llama-server-x86_64-pc-windows-msvc.exe") - } else { - // In Tauri 2, sidecar is managed via tauri-plugin-shell-sidecar. - // But if you're spawning manually, you need the absolute path in Resources. - let res_dir = app_handle.path().resource_dir().expect("Failed to get resource dir"); - // Binaries are typically in resources/bin or root resources - let candidate_a = res_dir.join("bin").join("llama-server-x86_64-pc-windows-msvc.exe"); - let candidate_b = res_dir.join("llama-server-x86_64-pc-windows-msvc.exe"); - if candidate_a.exists() { candidate_a } else { candidate_b } + // Prepare arguments + let args = [ + "--model", "models/embeddinggemma-300m-q4_0.gguf", // This might need absolute path if CWD is not root + "--port", "8080", + "--embedding", + "--host", "127.0.0.1", + "-c", "8192", "-b", "8192", "-ub", "8192", + "--parallel", "1" + ]; + + // Note: Tauri's sidecar() handles the library path (DLLs) if they are in the same folder + // as the sidecar binary in the App bundle. + + let (mut rx, child) = match sidecar.args(args).spawn() { + Ok(res) => res, + Err(e) => { + eprintln!("CRITICAL: Failed to spawn sidecar: {}", e); + return; + } }; - let mut cmd = std::process::Command::new(&sidecar_exe); - cmd.args(&["--model", &model_path.to_string_lossy(), "--port", "8080", "--embedding", "--host", "127.0.0.1", "-c", "8192", "-b", "8192", "-ub", "8192", "--parallel", "1"]); - - // Add bin dir to PATH so DLLs in the same folder are found - if let Some(bin_dir) = sidecar_exe.parent() { - let path = env::var("PATH").unwrap_or_default(); - cmd.env("PATH", format!("{};{}", bin_dir.display(), path)); - cmd.current_dir(bin_dir); - } - - // Also check resource_dir for DLLs - if let Ok(res_dir) = app_handle.path().resource_dir() { - if let Some(old_path) = cmd.get_envs().find(|(k, _)| k == "PATH").and_then(|(_, v)| v) { - cmd.env("PATH", format!("{};{}", res_dir.display(), old_path.to_string_lossy())); - } - } + println!("llama-server started (Sidecar)"); - println!("DEBUG: Spawning llama-server: {:?}", cmd); - match cmd.spawn() { - Ok(child) => { - let pid = child.id(); - println!("llama-server started (PID: {})", pid); - std::thread::spawn(move || { - match child.wait_with_output() { - Ok(out) => println!("llama-server exited (OK): {:?}", out.status), - Err(e) => eprintln!("llama-server error during wait: {}", e), + // Handle output in background + tauri::async_runtime::spawn(async move { + while let Some(event) = rx.recv().await { + match event { + tauri_plugin_shell::process::CommandEvent::Stdout(line) => { + let s = String::from_utf8_lossy(&line); + if s.contains("HTTP server listening") { + println!("llama-server: Ready"); + } } - }); + tauri_plugin_shell::process::CommandEvent::Stderr(line) => { + eprintln!("llama-server error: {}", String::from_utf8_lossy(&line)); + } + tauri_plugin_shell::process::CommandEvent::Terminated(status) => { + println!("llama-server terminated with status: {:?}", status.code); + break; + } + _ => {} + } } - Err(e) => eprintln!("CRITICAL: Failed to spawn llama-server: {}. Exe path: {:?}", e, sidecar_exe), - } + }); } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() - .invoke_handler(tauri::generate_handler![get_mcp_info, get_db_stats, get_sidecar_status]) + .plugin(tauri_plugin_shell::init()) .on_window_event(|window, event| { if let tauri::WindowEvent::CloseRequested { api, .. } = event { api.prevent_close(); @@ -258,16 +230,35 @@ } }) .setup(|app| { + // 1. Initialize Logging FIRST for crash diagnostics + let mut log_builder = tauri_plugin_log::Builder::default() + .targets([ + tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout), + tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }), + ]) + .max_file_size(10 * 1024 * 1024) + .level(log::LevelFilter::Info); + + if cfg!(debug_assertions) { + log_builder = log_builder.target(tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Folder { path: std::path::PathBuf::from("logs"), file_name: None })); + } + let _ = app.handle().plugin(log_builder.build()); + log::info!("Application starting (Tauri 2)..."); + + // 2. Load env dotenv().ok(); let app_handle = app.handle().clone(); - let _ = app.handle().plugin(tauri_plugin_shell::init()); + // 3. Start Sidecar let config = get_config(&app_handle); spawn_llama_server(&app_handle, &config); + // 4. Database and MCP initialization tauri::async_runtime::block_on(async move { let db_path = resolve_db_path(&app_handle, &config); let ext_path = resolve_extension_path(&app_handle); + log::info!("DB Path: {}, Ext Path: {}", db_path, ext_path); + let vec_dim = env::var("VEC_DIM").unwrap_or_else(|_| "768".to_string()).parse::().unwrap_or(768); let conn = db::init_db(&db_path, &ext_path, vec_dim).await.expect("Failed to init db"); @@ -287,18 +278,7 @@ tokio::spawn(async move { mcp::start_mcp_server(state, port).await; }); }); - let mut log_builder = tauri_plugin_log::Builder::default() - .targets([ - tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout), - tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }), - ]) - .max_file_size(10 * 1024 * 1024) - .level(log::LevelFilter::Info); - if cfg!(debug_assertions) { - log_builder = log_builder.target(tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Folder { path: PathBuf::from("logs"), file_name: None })); - } - let _ = app.handle().plugin(log_builder.build()); - + // 5. Tray setup let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?; let show_i = MenuItem::with_id(app, "show", "Show", true, None::<&str>)?; let menu = Menu::with_items(app, &[&show_i, &quit_i])?; @@ -315,8 +295,11 @@ if let Some(w) = app.get_webview_window("main") { let _ = w.show(); let _ = w.set_focus(); } }) .build(app)?; + + log::info!("Tauri setup completed"); Ok(()) }) + .invoke_handler(tauri::generate_handler![get_mcp_info, get_db_stats, get_sidecar_status]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src/backend/tauri.conf.json b/src/backend/tauri.conf.json index 91a2aaa..b65e60e 100644 --- a/src/backend/tauri.conf.json +++ b/src/backend/tauri.conf.json @@ -27,7 +27,7 @@ "bundle": { "active": true, "targets": "all", - "externalBin": ["../../bin/llama-server-x86_64-pc-windows-msvc"], + "externalBin": ["../../bin/llama-server"], "resources": ["../../resources/*", "../../bin/*.dll"], "icon": [ "../../resources/icons/32x32.png",