diff --git a/.forgejo/workflows/botserver.yaml b/.forgejo/workflows/botserver.yaml index 4e150c1c..ba3e37ac 100644 --- a/.forgejo/workflows/botserver.yaml +++ b/.forgejo/workflows/botserver.yaml @@ -83,6 +83,9 @@ jobs: working-directory: /opt/gbo/data/botserver run: | sccache --start-server 2>/dev/null || true + BOTSERVER_BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" + export BOTSERVER_BUILD_DATE + echo "Build date: $BOTSERVER_BUILD_DATE" cargo build -p botserver -j 8 2>&1 | tee /tmp/build.log sccache --show-stats ls -lh target/debug/botserver diff --git a/src/llm/llm_models/deepseek_r3.rs b/src/llm/llm_models/deepseek_r3.rs index 578505f0..a6009801 100644 --- a/src/llm/llm_models/deepseek_r3.rs +++ b/src/llm/llm_models/deepseek_r3.rs @@ -1,10 +1,9 @@ use super::ModelHandler; -use std::sync::LazyLock; use regex::Regex; +use std::sync::LazyLock; -static THINK_TAG_REGEX: LazyLock> = LazyLock::new(|| { - 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 { diff --git a/src/llm/llm_models/gpt_oss_20b.rs b/src/llm/llm_models/gpt_oss_20b.rs index 385fe118..c3234bd3 100644 --- a/src/llm/llm_models/gpt_oss_20b.rs +++ b/src/llm/llm_models/gpt_oss_20b.rs @@ -1,18 +1,25 @@ use super::deepseek_r3::strip_think_tags; use super::ModelHandler; -use std::sync::LazyLock; use regex::Regex; +use std::sync::LazyLock; -static ANALYSIS_MARKER_REGEX: LazyLock> = LazyLock::new(|| { - Regex::new(r"analysis<\|message\|>") -}); +static ANALYSIS_MARKER_REGEX: LazyLock> = + LazyLock::new(|| Regex::new(r"analysis<\|message\|>")); + +static FINAL_MARKER_REGEX: LazyLock> = + LazyLock::new(|| Regex::new(r"<\|message\|>final<\|message\|>")); #[derive(Debug)] pub struct GptOss20bHandler; impl ModelHandler for GptOss20bHandler { fn is_analysis_complete(&self, buffer: &str) -> bool { - buffer.contains("final") || buffer.contains("") + buffer.contains("") + || (if let Ok(re) = &*FINAL_MARKER_REGEX { + re.is_match(buffer) + } else { + false + }) } fn process_content(&self, content: &str) -> String { @@ -20,9 +27,10 @@ impl ModelHandler for GptOss20bHandler { if without_think.is_empty() { return String::new(); } - match without_think.find("final") { - Some(pos) => without_think[..pos].to_string(), - None => without_think, + if let Ok(re) = &*FINAL_MARKER_REGEX { + re.replace_all(&without_think, "").to_string() + } else { + without_think } } @@ -31,6 +39,6 @@ impl ModelHandler for GptOss20bHandler { re.is_match(buffer) } else { buffer.contains("analysis<|message|>") - }) || buffer.contains("") + }) || buffer.contains("") } } diff --git a/src/llm/llm_models/mod.rs b/src/llm/llm_models/mod.rs index 04a662b2..873316a1 100644 --- a/src/llm/llm_models/mod.rs +++ b/src/llm/llm_models/mod.rs @@ -8,6 +8,23 @@ pub trait ModelHandler: Send + Sync { fn has_analysis_markers(&self, buffer: &str) -> bool; } +#[derive(Debug)] +pub struct PassthroughHandler; + +impl ModelHandler for PassthroughHandler { + fn is_analysis_complete(&self, _buffer: &str) -> bool { + true + } + + fn process_content(&self, content: &str) -> String { + content.to_string() + } + + fn has_analysis_markers(&self, _buffer: &str) -> bool { + false + } +} + #[must_use] pub fn get_handler(model_path: &str) -> Box { let path = model_path.to_lowercase(); @@ -15,7 +32,9 @@ pub fn get_handler(model_path: &str) -> Box { Box::new(deepseek_r3::DeepseekR3Handler) } else if path.contains("120b") { Box::new(gpt_oss_120b::GptOss120bHandler::new()) - } else { + } else if path.contains("20b") { Box::new(gpt_oss_20b::GptOss20bHandler) + } else { + Box::new(PassthroughHandler) } } diff --git a/src/main_module/health.rs b/src/main_module/health.rs index 66a2df26..51ed305c 100644 --- a/src/main_module/health.rs +++ b/src/main_module/health.rs @@ -17,12 +17,16 @@ pub async fn health_check(State(state): State>) -> (StatusCode, Js StatusCode::SERVICE_UNAVAILABLE }; + // Build timestamp set by CI via BOTSERVER_BUILD_DATE env var + let build_date = option_env!("BOTSERVER_BUILD_DATE").unwrap_or("unknown"); + ( code, Json(serde_json::json!({ "status": status, "service": "botserver", "version": env!("CARGO_PKG_VERSION"), + "build_date": build_date, "database": db_ok })), )