diff --git a/src/drive/drive_monitor/mod.rs b/src/drive/drive_monitor/mod.rs index dcb96f5a..781fe233 100644 --- a/src/drive/drive_monitor/mod.rs +++ b/src/drive/drive_monitor/mod.rs @@ -1096,8 +1096,8 @@ let file_state = FileState { if parts.len() >= 2 { let prompt_filename = parts[1].trim(); if !prompt_filename.is_empty() { - // Get prompt file from MinIO - let prompt_key = format!("salesianos.gbot/{}", prompt_filename); + let bot_name = self.bucket_name.strip_suffix(".gbai").unwrap_or(&self.bucket_name); + let prompt_key = format!("{}.gbot/{}", bot_name, prompt_filename); if let Ok(prompt_response) = client .get_object() .bucket(&self.bucket_name) @@ -1110,7 +1110,6 @@ let file_state = FileState { .map_err(|_e| format!("UTF-8 error in {}", prompt_filename))?; // Save to work directory - 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)); if let Err(e) = tokio::task::spawn_blocking({ diff --git a/src/llm/claude.rs b/src/llm/claude.rs index fcd0da50..bec15736 100644 --- a/src/llm/claude.rs +++ b/src/llm/claude.rs @@ -332,7 +332,6 @@ impl ClaudeClient { let text = delta .content .as_deref() - .or(delta.reasoning_content.as_deref()) .unwrap_or(""); if !text.is_empty() { diff --git a/src/llm/llm_models/deepseek_r3.rs b/src/llm/llm_models/deepseek_r3.rs index 5155b780..578505f0 100644 --- a/src/llm/llm_models/deepseek_r3.rs +++ b/src/llm/llm_models/deepseek_r3.rs @@ -1,24 +1,31 @@ - use super::ModelHandler; use std::sync::LazyLock; +use regex::Regex; -static THINK_TAG_REGEX: LazyLock> = LazyLock::new(|| { - regex::Regex::new(r"(?s).*?") +static THINK_TAG_REGEX: LazyLock> = LazyLock::new(|| { + Regex::new(r"(?s).*?") }); +pub fn strip_think_tags(content: &str) -> String { + if let Ok(re) = &*THINK_TAG_REGEX { + re.replace_all(content, "").to_string() + } else { + content.to_string() + } +} + #[derive(Debug)] pub struct DeepseekR3Handler; + impl ModelHandler for DeepseekR3Handler { fn is_analysis_complete(&self, buffer: &str) -> bool { buffer.contains("") } + fn process_content(&self, content: &str) -> String { - if let Ok(re) = &*THINK_TAG_REGEX { - re.replace_all(content, "").to_string() - } else { - content.to_string() - } + strip_think_tags(content) } + fn has_analysis_markers(&self, buffer: &str) -> bool { buffer.contains("") } diff --git a/src/llm/llm_models/gpt_oss_120b.rs b/src/llm/llm_models/gpt_oss_120b.rs index de1d0f78..c6b364ae 100644 --- a/src/llm/llm_models/gpt_oss_120b.rs +++ b/src/llm/llm_models/gpt_oss_120b.rs @@ -1,4 +1,4 @@ - +use super::deepseek_r3::strip_think_tags; use super::ModelHandler; #[derive(Debug)] pub struct GptOss120bHandler {} @@ -15,12 +15,14 @@ impl GptOss120bHandler { } impl ModelHandler for GptOss120bHandler { fn is_analysis_complete(&self, buffer: &str) -> bool { - buffer.contains("**end**") + buffer.contains("**end**") || buffer.contains("") } fn process_content(&self, content: &str) -> String { - content.replace("**start**", "").replace("**end**", "") + strip_think_tags(content) + .replace("**start**", "") + .replace("**end**", "") } fn has_analysis_markers(&self, buffer: &str) -> bool { - buffer.contains("**start**") + buffer.contains("**start**") || buffer.contains("") } } diff --git a/src/llm/llm_models/gpt_oss_20b.rs b/src/llm/llm_models/gpt_oss_20b.rs index 7edccc1f..385fe118 100644 --- a/src/llm/llm_models/gpt_oss_20b.rs +++ b/src/llm/llm_models/gpt_oss_20b.rs @@ -1,16 +1,36 @@ +use super::deepseek_r3::strip_think_tags; use super::ModelHandler; +use std::sync::LazyLock; +use regex::Regex; + +static ANALYSIS_MARKER_REGEX: LazyLock> = LazyLock::new(|| { + Regex::new(r"analysis<\|message\|>") +}); + #[derive(Debug)] pub struct GptOss20bHandler; + impl ModelHandler for GptOss20bHandler { fn is_analysis_complete(&self, buffer: &str) -> bool { - buffer.ends_with("final") + buffer.contains("final") || buffer.contains("") } + fn process_content(&self, content: &str) -> String { - content - .find("final") - .map_or_else(|| content.to_string(), |pos| content[..pos].to_string()) + let without_think = strip_think_tags(content); + if without_think.is_empty() { + return String::new(); + } + match without_think.find("final") { + Some(pos) => without_think[..pos].to_string(), + None => without_think, + } } + fn has_analysis_markers(&self, buffer: &str) -> bool { - buffer.contains("analysis<|message|>") + (if let Ok(re) = &*ANALYSIS_MARKER_REGEX { + re.is_match(buffer) + } else { + buffer.contains("analysis<|message|>") + }) || buffer.contains("") } } diff --git a/src/llm/mod.rs b/src/llm/mod.rs index 49616311..0182610a 100644 --- a/src/llm/mod.rs +++ b/src/llm/mod.rs @@ -458,10 +458,7 @@ impl LLMProvider for OpenAIClient { for line in chunk_str.lines() { if line.starts_with("data: ") && !line.contains("[DONE]") { if let Ok(data) = serde_json::from_str::(&line[6..]) { - // Handle content (standard) or reasoning/reasoning_content (NVIDIA reasoning models) - let content = data["choices"][0]["delta"]["content"].as_str() - .or_else(|| data["choices"][0]["delta"]["reasoning_content"].as_str()) - .or_else(|| data["choices"][0]["delta"]["reasoning"].as_str()); + let content = data["choices"][0]["delta"]["content"].as_str(); if let Some(content) = content { let processed = handler.process_content(content); if !processed.is_empty() {