diff --git a/src/basic/keywords/use_tool.rs b/src/basic/keywords/use_tool.rs index d8833ea7..7f61d236 100644 --- a/src/basic/keywords/use_tool.rs +++ b/src/basic/keywords/use_tool.rs @@ -144,15 +144,11 @@ fn associate_tool_with_session( use crate::core::shared::models::schema::session_tool_associations; // Check if tool's .mcp.json file exists in work directory - // Use relative path from botserver binary current directory - let gb_dir = - std::path::PathBuf::from(crate::core::shared::utils::get_stack_path()).join("data/system"); + let work_root = crate::core::shared::utils::get_work_path(); // Get bot name to construct the path let bot_name = get_bot_name_from_id(state, &user.bot_id)?; - let work_path = Path::new(&gb_dir) - .join("work") - .join(format!("{}.gbai/{}.gbdialog", bot_name, bot_name)); + let work_path = Path::new(&work_root).join(format!("{}.gbai/{}.gbdialog", bot_name, bot_name)); let mcp_path = work_path.join(format!("{}.mcp.json", tool_name)); trace!("Checking for tool .mcp.json at: {:?}", mcp_path); diff --git a/src/core/bot/tool_context.rs b/src/core/bot/tool_context.rs index f0cebd8e..6cc03381 100644 --- a/src/core/bot/tool_context.rs +++ b/src/core/bot/tool_context.rs @@ -35,19 +35,16 @@ pub fn get_session_tools( } // Build path to work/{bot_name}.gbai/{bot_name}.gbdialog directory - // Use relative path from botserver binary location - let gb_dir = - std::path::PathBuf::from(crate::core::shared::utils::get_stack_path()).join("data/system"); + let work_root = std::path::PathBuf::from(crate::core::shared::utils::get_work_path()); // Ensure work directory exists (create if not) - let work_base = gb_dir.join("work"); - if !work_base.exists() { - std::fs::create_dir_all(&work_base) - .map_err(|e| format!("Failed to create work directory {:?}: {}", work_base, e))?; - info!("Created work directory at: {:?}", work_base); + if !work_root.exists() { + std::fs::create_dir_all(&work_root) + .map_err(|e| format!("Failed to create work directory {:?}: {}", work_root, e))?; + info!("Created work directory at: {:?}", work_root); } - let work_path = work_base.join(format!("{}.gbai/{}.gbdialog", bot_name, bot_name)); + let work_path = work_root.join(format!("{}.gbai/{}.gbdialog", bot_name, bot_name)); info!( "Loading {} tools for session {} from {:?}", diff --git a/src/drive/drive_monitor/mod.rs b/src/drive/drive_monitor/mod.rs index 0b70d47e..8312ebe0 100644 --- a/src/drive/drive_monitor/mod.rs +++ b/src/drive/drive_monitor/mod.rs @@ -600,35 +600,50 @@ impl DriveMonitor { } if is_prompt_file { - // Download prompt file to work directory - match client.get_object().bucket(&self.bucket_name).key(&path).send().await { - Ok(response) => { - let bytes = response.body.collect().await?.into_bytes(); - let content = String::from_utf8(bytes.to_vec()) - .map_err(|e| format!("UTF-8 error in {}: {}", path, e))?; - let bot_name = self.bucket_name.strip_suffix(".gbai").unwrap_or(&self.bucket_name); - let gbot_dir = self.work_root.join(format!("{}.gbai/{}.gbot", bot_name, bot_name)); - let path_buf = PathBuf::from(&path); - let file_name = path_buf.file_name() - .and_then(|n| n.to_str()).unwrap_or("PROMPT.md"); - if let Err(e) = tokio::task::spawn_blocking({ - let gbot_dir_str = gbot_dir.to_string_lossy().to_string(); - let file_name_owned = file_name.to_string(); - let content_owned = content.clone(); - move || { - std::fs::create_dir_all(&gbot_dir_str)?; - std::fs::write(format!("{}/{}", gbot_dir_str, file_name_owned), &content_owned)?; - Ok::<(), Box>(()) + // Check etag to avoid re-downloading unchanged prompt files + let etag = obj.e_tag().unwrap_or_default().to_string(); + let prompt_state_key = format!("__prompt__{}", path); + let should_download = { + let states = self.file_states.read().await; + match states.get(&prompt_state_key) { + Some(prev) => prev.etag != etag, + None => true, + } + }; + if should_download { + match client.get_object().bucket(&self.bucket_name).key(&path).send().await { + Ok(response) => { + let bytes = response.body.collect().await?.into_bytes(); + let content = String::from_utf8(bytes.to_vec()) + .map_err(|e| format!("UTF-8 error in {}: {}", path, e))?; + let bot_name = self.bucket_name.strip_suffix(".gbai").unwrap_or(&self.bucket_name); + let gbot_dir = self.work_root.join(format!("{}.gbai/{}.gbot", bot_name, bot_name)); + let path_buf = PathBuf::from(&path); + let file_name = path_buf.file_name() + .and_then(|n| n.to_str()).unwrap_or("PROMPT.md"); + if let Err(e) = tokio::task::spawn_blocking({ + let gbot_dir_str = gbot_dir.to_string_lossy().to_string(); + let file_name_owned = file_name.to_string(); + let content_owned = content.clone(); + move || { + std::fs::create_dir_all(&gbot_dir_str)?; + std::fs::write(format!("{}/{}", gbot_dir_str, file_name_owned), &content_owned)?; + Ok::<(), Box>(()) + } + }).await { + log::error!("Failed to save prompt file: {}", e); + } else { + log::info!("Downloaded prompt file {} to work directory", path); } - }).await { - log::error!("Failed to save prompt file: {}", e); - } else { - log::info!("Downloaded prompt file {} to work directory", path); + } + Err(e) => { + log::error!("Failed to download prompt file {}: {}", path, e); } } - Err(e) => { - log::error!("Failed to download prompt file {}: {}", path, e); - } + let mut states = self.file_states.write().await; + states.insert(prompt_state_key, FileState { etag, indexed: false }); + } else { + trace!("Prompt file {} unchanged (etag match), skipping download", path); } continue; }