From 9016fbff682b086469d5a77db6eb2cec2acdf20f Mon Sep 17 00:00:00 2001 From: Akarshan Biswas Date: Tue, 6 May 2025 20:04:25 +0530 Subject: [PATCH] feat: inference-llamacpp-extension: backend implementation --- .../inference_llamacpp_extension/mod.rs | 1 + .../inference_llamacpp_extension/server.rs | 127 ++++++++++++++++++ src-tauri/src/core/utils/extensions/mod.rs | 1 + src-tauri/src/core/utils/mod.rs | 1 + src-tauri/src/lib.rs | 3 + 5 files changed, 133 insertions(+) create mode 100644 src-tauri/src/core/utils/extensions/inference_llamacpp_extension/mod.rs create mode 100644 src-tauri/src/core/utils/extensions/inference_llamacpp_extension/server.rs create mode 100644 src-tauri/src/core/utils/extensions/mod.rs diff --git a/src-tauri/src/core/utils/extensions/inference_llamacpp_extension/mod.rs b/src-tauri/src/core/utils/extensions/inference_llamacpp_extension/mod.rs new file mode 100644 index 000000000..bfe15ae42 --- /dev/null +++ b/src-tauri/src/core/utils/extensions/inference_llamacpp_extension/mod.rs @@ -0,0 +1 @@ +pub mod server; \ No newline at end of file diff --git a/src-tauri/src/core/utils/extensions/inference_llamacpp_extension/server.rs b/src-tauri/src/core/utils/extensions/inference_llamacpp_extension/server.rs new file mode 100644 index 000000000..0dba7fa6a --- /dev/null +++ b/src-tauri/src/core/utils/extensions/inference_llamacpp_extension/server.rs @@ -0,0 +1,127 @@ +use std::path::PathBuf; +use std::sync::Arc; +use tauri::{AppHandle, Manager, State}; // Import Manager trait +use tokio::process::{Child, Command}; +use tokio::sync::Mutex; + +use super::state::AppState; + +// Error type for server commands +#[derive(Debug, thiserror::Error)] +pub enum ServerError { + #[error("Server is already running")] + AlreadyRunning, + #[error("Server is not running")] + NotRunning, + #[error("Failed to locate server binary: {0}")] + BinaryNotFound(String), + #[error("Failed to determine resource path: {0}")] + ResourcePathError(String), + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + #[error("Jan API error: {0}")] + Tauri(#[from] tauri::Error), +} + +type ServerResult = Result; + +// --- Helper function to find the server binary --- +// -- TODO: Adjust extension engine paths +// engine: static llama-server build (CUDA, VULKAN, SYCL, etc) +fn get_server_path(app_handle: &AppHandle) -> ServerResult { + let binary_name = if cfg!(windows) { + "llama-server.exe" + } else { + "llama-server" + }; + let relative_path = PathBuf::from("engines").join(binary_name); // TODO: ADJUST THIS PATH + + app_handle + .path() + .resolve_resource(relative_path) + .map_err(|e| ServerError::ResourcePathError(e.to_string()))? + .ok_or_else(|| { + ServerError::BinaryNotFound(format!( + "Could not resolve resource path for '{}'", + if cfg!(windows) { + "engines/llama-server.exe" + } else { + "engines/llama-server" + } // TODO: ADJUST THIS PATH + )) + }) +} + +// --- Load Command --- +#[tauri::command] +pub async fn load( + app_handle: AppHandle, // Get the AppHandle + state: State<'_, AppState>, // Access the shared state + args: Vec, // Arguments from the frontend +) -> ServerResult<()> { + let mut process_lock = state.llama_server_process.lock().await; + + if process_lock.is_some() { + log::warn!("Attempted to load server, but it's already running."); + return Err(ServerError::AlreadyRunning); + } + + let server_path = get_server_path(&app_handle)?; + log::info!("Attempting to launch server at path: {:?}", server_path); + log::info!("Using arguments: {:?}", args); + + if !server_path.exists() { + log::error!("Server binary not found at expected path: {:?}", server_path); + return Err(ServerError::BinaryNotFound(format!("Binary not found at {:?}", server_path))); + } + + // Configure the command to run the server + let mut command = Command::new(server_path); + command.args(args); + + // Optional: Redirect stdio if needed (e.g., for logging within Jan) + // command.stdout(Stdio::piped()); + // command.stderr(Stdio::piped()); + + // Spawn the child process + let child = command.spawn().map_err(ServerError::Io)?; + + log::info!("Server process started with PID: {:?}", child.id()); + + // Store the child process handle in the state + *process_lock = Some(child); + + Ok(()) +} + +// --- Unload Command --- +#[tauri::command] +pub async fn unload(state: State<'_, AppState>) -> ServerResult<()> { + let mut process_lock = state.llama_server_process.lock().await; + + // Take the child process out of the Option, leaving None in its place + if let Some(mut child) = process_lock.take() { + log::info!( + "Attempting to terminate server process with PID: {:?}", + child.id() + ); + // Kill the process + // `start_kill` is preferred in async contexts + match child.start_kill() { + Ok(_) => { + log::info!("Server process termination signal sent."); + Ok(()) + } + Err(e) => { + // For simplicity, we log and return error. + log::error!("Failed to kill server process: {}", e); + // Put it back? Maybe not useful if kill failed. + // *process_lock = Some(child); + Err(ServerError::Io(e)) + } + } + } else { + log::warn!("Attempted to unload server, but it was not running."); + Ok(()) + } +} \ No newline at end of file diff --git a/src-tauri/src/core/utils/extensions/mod.rs b/src-tauri/src/core/utils/extensions/mod.rs new file mode 100644 index 000000000..eae0bbe8f --- /dev/null +++ b/src-tauri/src/core/utils/extensions/mod.rs @@ -0,0 +1 @@ +pub mod inference_llamacpp_extension; \ No newline at end of file diff --git a/src-tauri/src/core/utils/mod.rs b/src-tauri/src/core/utils/mod.rs index 04bfd12b0..faf8aeda2 100644 --- a/src-tauri/src/core/utils/mod.rs +++ b/src-tauri/src/core/utils/mod.rs @@ -76,3 +76,4 @@ pub fn normalize_path(path: &Path) -> PathBuf { } ret } +pub mod extensions; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0ef6f059a..fa6ccaea4 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -87,6 +87,9 @@ pub fn run() { // hardware core::hardware::get_system_info, core::hardware::get_system_usage, + // llama-cpp extension + core::utils::extensions::inference_llamacpp_extension::load, + core::utils::extensions::inference_llamacpp_extension::unload ]) .manage(AppState { app_token: Some(generate_app_token()),