diff --git a/app/MindWork AI Studio/Pages/Information.razor b/app/MindWork AI Studio/Pages/Information.razor
index bc764560..cd97d457 100644
--- a/app/MindWork AI Studio/Pages/Information.razor
+++ b/app/MindWork AI Studio/Pages/Information.razor
@@ -239,7 +239,7 @@
-
+
diff --git a/documentation/Build.md b/documentation/Build.md
index 21063eef..582270e0 100644
--- a/documentation/Build.md
+++ b/documentation/Build.md
@@ -45,7 +45,19 @@ Do you want to test your changes before creating a PR? Follow these steps:
9. Execute the command `dotnet run`.
10. After compiling the .NET code, the app will finally start inside the Tauri runtime window.
-You can now test your changes.
+You can now test your changes. To stop the application:
+- Close the Tauri window (GUI).
+- Press ``Ctrl+C`` in the terminal where the app is running.
+- Stop the process via your IDE’s run/debug controls.
+
+> ⚠️ Important: Stopping the app via ``Ctrl+C`` or the IDE may not terminate the Qdrant sidecar process, especially on Windows. This can lead to startup failures when restarting the app.
+
+If you encounter issues restarting Tauri:
+Manually kill the Qdrant process:
+**Linux/macOS:** Run pkill -f qdrant in your terminal.
+**Windows:** Open Task Manager → Find qdrant.exe → Right-click → “End task”.
+
+Restart your Tauri app.
## Create a release
In order to create a release:
diff --git a/runtime/src/dotnet.rs b/runtime/src/dotnet.rs
index c1e0b560..338074a0 100644
--- a/runtime/src/dotnet.rs
+++ b/runtime/src/dotnet.rs
@@ -17,6 +17,7 @@ use crate::environment::{is_dev, DATA_DIRECTORY};
use crate::network::get_available_port;
use crate::runtime_api::API_SERVER_PORT;
use crate::stale_process_cleanup::{kill_stale_process, log_potential_stale_process};
+use crate::sidecar_types::SidecarType;
// The .NET server is started in a separate process and communicates with this
// runtime process via IPC. However, we do net start the .NET server in
@@ -30,6 +31,7 @@ static DOTNET_SERVER_PORT: Lazy = Lazy::new(|| get_available_port().unwrap(
static DOTNET_INITIALIZED: Lazy> = Lazy::new(|| Mutex::new(false));
pub const PID_FILE_NAME: &str = "mindwork_ai_studio.pid";
+const SIDECAR_TYPE:SidecarType = SidecarType::Dotnet;
/// Returns the desired port of the .NET server. Our .NET app calls this endpoint to get
/// the port where the .NET server should listen to.
@@ -100,7 +102,7 @@ pub fn start_dotnet_server() {
.expect("Failed to spawn .NET server process.");
let server_pid = child.pid();
info!(Source = "Bootloader .NET"; "The .NET server process started with PID={server_pid}.");
- log_potential_stale_process(Path::new(DATA_DIRECTORY.get().unwrap()).join(PID_FILE_NAME), server_pid);
+ log_potential_stale_process(Path::new(DATA_DIRECTORY.get().unwrap()).join(PID_FILE_NAME), server_pid, SIDECAR_TYPE);
// Save the server process to stop it later:
*server_spawn_clone.lock().unwrap() = Some(child);
@@ -165,7 +167,7 @@ pub fn stop_dotnet_server() {
/// Remove old Pid files and kill the corresponding processes
pub fn cleanup_dotnet_server() {
let pid_path = Path::new(DATA_DIRECTORY.get().unwrap()).join(PID_FILE_NAME);
- if let Err(e) = kill_stale_process(pid_path) {
+ if let Err(e) = kill_stale_process(pid_path, SIDECAR_TYPE) {
warn!(Source = ".NET"; "Error during the cleanup of .NET: {}", e);
}
}
\ No newline at end of file
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 5447395b..1b13e099 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -16,4 +16,5 @@ pub mod pandoc;
pub mod qdrant;
pub mod certificate_factory;
pub mod runtime_api_token;
-pub mod stale_process_cleanup;
\ No newline at end of file
+pub mod stale_process_cleanup;
+mod sidecar_types;
\ No newline at end of file
diff --git a/runtime/src/qdrant.rs b/runtime/src/qdrant.rs
index c562dc75..41429431 100644
--- a/runtime/src/qdrant.rs
+++ b/runtime/src/qdrant.rs
@@ -17,6 +17,7 @@ use crate::certificate_factory::generate_certificate;
use std::path::PathBuf;
use tempfile::{TempDir, Builder};
use crate::stale_process_cleanup::{kill_stale_process, log_potential_stale_process};
+use crate::sidecar_types::SidecarType;
// Qdrant server process started in a separate process and can communicate
// via HTTP or gRPC with the .NET server and the runtime process
@@ -39,6 +40,7 @@ static API_TOKEN: Lazy = Lazy::new(|| {
static TMPDIR: Lazy>> = Lazy::new(|| Mutex::new(None));
const PID_FILE_NAME: &str = "qdrant.pid";
+const SIDECAR_TYPE:SidecarType = SidecarType::Qdrant;
#[derive(Serialize)]
pub struct ProvideQdrantInfo {
@@ -76,7 +78,7 @@ pub fn start_qdrant_server(){
let snapshot_path = path.join("snapshots").to_str().unwrap().to_string();
let init_path = path.join(".qdrant-initalized").to_str().unwrap().to_string();
- let mut qdrant_server_environment = HashMap::from_iter([
+ let qdrant_server_environment = HashMap::from_iter([
(String::from("QDRANT__SERVICE__HTTP_PORT"), QDRANT_SERVER_PORT_HTTP.to_string()),
(String::from("QDRANT__SERVICE__GRPC_PORT"), QDRANT_SERVER_PORT_GRPC.to_string()),
(String::from("QDRANT_INIT_FILE_PATH"), init_path),
@@ -90,13 +92,6 @@ pub fn start_qdrant_server(){
let server_spawn_clone = QDRANT_SERVER.clone();
tauri::async_runtime::spawn(async move {
- #[cfg(target_os = "macos")]
- {
- qdrant_server_environment.insert(
- "MALLOC_CONF".to_string(),
- "background_thread:false".to_string(),
- );
- }
let (mut rx, child) = Command::new_sidecar("qdrant")
.expect("Failed to create sidecar for Qdrant")
.args(["--config-path", "resources/databases/qdrant/config.yaml"])
@@ -106,7 +101,7 @@ pub fn start_qdrant_server(){
let server_pid = child.pid();
info!(Source = "Bootloader Qdrant"; "Qdrant server process started with PID={server_pid}.");
- log_potential_stale_process(path.join(PID_FILE_NAME), server_pid);
+ log_potential_stale_process(path.join(PID_FILE_NAME), server_pid, SIDECAR_TYPE);
// Save the server process to stop it later:
*server_spawn_clone.lock().unwrap() = Some(child);
@@ -193,7 +188,7 @@ pub fn drop_tmpdir() {
/// Remove old Pid files and kill the corresponding processes
pub fn cleanup_qdrant() {
let pid_path = Path::new(DATA_DIRECTORY.get().unwrap()).join("databases").join("qdrant").join(PID_FILE_NAME);
- if let Err(e) = kill_stale_process(pid_path) {
+ if let Err(e) = kill_stale_process(pid_path, SIDECAR_TYPE) {
warn!(Source = "Qdrant"; "Error during the cleanup of Qdrant: {}", e);
}
if let Err(e) = delete_old_certificates() {
diff --git a/runtime/src/sidecar_types.rs b/runtime/src/sidecar_types.rs
new file mode 100644
index 00000000..7e5bfde0
--- /dev/null
+++ b/runtime/src/sidecar_types.rs
@@ -0,0 +1,15 @@
+use std::fmt;
+
+pub enum SidecarType {
+ Dotnet,
+ Qdrant,
+}
+
+impl fmt::Display for SidecarType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ SidecarType::Dotnet => write!(f, ".Net"),
+ SidecarType::Qdrant => write!(f, "Qdrant"),
+ }
+ }
+}
\ No newline at end of file
diff --git a/runtime/src/stale_process_cleanup.rs b/runtime/src/stale_process_cleanup.rs
index 124f576c..7d177ac8 100644
--- a/runtime/src/stale_process_cleanup.rs
+++ b/runtime/src/stale_process_cleanup.rs
@@ -4,6 +4,7 @@ use std::io::{Error, ErrorKind, Write};
use std::path::{PathBuf};
use log::{info, warn};
use sysinfo::{Pid, ProcessesToUpdate, Signal, System};
+use crate::sidecar_types::SidecarType;
fn parse_pid_file(content: &str) -> Result<(u32, String), Error> {
let mut lines = content
@@ -23,7 +24,7 @@ fn parse_pid_file(content: &str) -> Result<(u32, String), Error> {
Ok((pid, name))
}
-pub fn kill_stale_process(pid_file_path: PathBuf) -> Result<(), Error> {
+pub fn kill_stale_process(pid_file_path: PathBuf, sidecar_type: SidecarType) -> Result<(), Error> {
if !pid_file_path.exists() {
return Ok(());
}
@@ -51,30 +52,24 @@ pub fn kill_stale_process(pid_file_path: PathBuf) -> Result<(), Error> {
if !killed {
return Err(Error::new(ErrorKind::Other, "Failed to kill process"));
}
-
- system.refresh_processes(ProcessesToUpdate::Some(&[pid]), true);
- if !system.process(pid).is_none() {
- return Err(Error::new(ErrorKind::Other, "Process still running after kill attempt"))
- }
- info!("Killed process: {}", pid_file_path.display());
+ info!(Source="Stale Process Cleanup";"{}: Killed process: \"{}\"", sidecar_type,pid_file_path.display());
} else {
- info!("Pid file {} was found, but process was not.", pid);
+ info!(Source="Stale Process Cleanup";"{}: Pid file with process number '{}' was found, but process was not.", sidecar_type, pid);
};
fs::remove_file(&pid_file_path)?;
- info!("Deleted redundant Pid file: {}", pid_file_path.display());
+ info!(Source="Stale Process Cleanup";"{}: Deleted redundant Pid file: \"{}\"", sidecar_type,pid_file_path.display());
Ok(())
}
-pub fn log_potential_stale_process(pid_file_path: PathBuf, pid: u32) {
+pub fn log_potential_stale_process(pid_file_path: PathBuf, pid: u32, sidecar_type: SidecarType) {
let mut system = System::new_all();
- let pid_u32 = pid;
- let pid = Pid::from_u32(pid_u32);
+ let pid = Pid::from_u32(pid);
system.refresh_processes(ProcessesToUpdate::Some(&[pid]), true);
let Some(process) = system.process(pid) else {
- warn!(
- "Pid file {} was not created because the process was not found.",
- pid_u32
+ warn!(Source="Stale Process Cleanup";
+ "{}: Pid file with process number '{}' was not created because the process was not found.",
+ sidecar_type, pid
);
return;
};
@@ -82,13 +77,13 @@ pub fn log_potential_stale_process(pid_file_path: PathBuf, pid: u32) {
match File::create(&pid_file_path) {
Ok(mut file) => {
let name = process.name().to_string_lossy();
- let content = format!("{pid_u32}\n{name}\n");
+ let content = format!("{pid}\n{name}\n");
if let Err(e) = file.write_all(content.as_bytes()) {
- warn!("Failed to write to {}: {}", pid_file_path.display(), e);
+ warn!(Source="Stale Process Cleanup";"{}: Failed to write to \"{}\": {}", sidecar_type,pid_file_path.display(), e);
}
}
Err(e) => {
- warn!("Failed to create {}: {}", pid_file_path.display(), e);
+ warn!(Source="Stale Process Cleanup";"{}: Failed to create \"{}\": {}", sidecar_type, pid_file_path.display(), e);
}
}
}