diff --git a/.cargo/config.toml b/.cargo/config.toml index df9d18e9..8493605a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,9 @@ [build] rustc-wrapper = "sccache" -jobs = 12 +jobs = 6 [target.x86_64-unknown-linux-gnu] linker = "clang" +rustflags = [ + "-C", "link-arg=-fuse-ld=mold" +] diff --git a/.forgejo/workflows/botserver.yaml b/.forgejo/workflows/botserver.yaml index 281d7f0c..861177e6 100644 --- a/.forgejo/workflows/botserver.yaml +++ b/.forgejo/workflows/botserver.yaml @@ -15,50 +15,50 @@ jobs: build: runs-on: gbo env: - CARGO_TARGET_DIR: /opt/gbo/work/target - RUSTC_WRAPPER: "" + RUSTC_WRAPPER: sccache + CARGO_INCREMENTAL: "0" PATH: /home/gbuser/.cargo/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/sbin:/bin - STAGE_SYSTEM_HOST: "system" - SYSTEM_USER: gbuser - CARGO_BUILD_JOBS=8 - steps: - name: Setup run: | cd /opt/gbo/work/generalbots - git reset --hard HEAD - git clean -fd git pull git submodule update --init --recursive - + git ls-files -z | xargs -0 touch -d "@$(git log -1 --format='%ct')" + git log --oneline -1 + - name: Build BotServer and BotUI run: | cd /opt/gbo/work/generalbots - cargo build -p botserver --bin botserver - cargo build -p botui --bin botui - + rustup default stable + export RUSTC_WRAPPER=sccache + echo "=== sccache stats before build ===" + sccache --show-stats 2>/dev/null || echo "sccache not available" + CARGO_BUILD_JOBS=6 cargo build -p botserver --bin botserver + CARGO_BUILD_JOBS=6 cargo build -p botui --bin botui --release + echo "=== sccache stats after build ===" + sccache --show-stats 2>/dev/null || echo "sccache not available" + - name: Deploy to Stage run: | - echo "=== Deploying BotServer and BotUI to Stage ===" - - # Copy both binaries to stage system container + echo "=== Deploying to Stage ===" scp -i /home/gbuser/.ssh/id_ed25519 -o StrictHostKeyChecking=no \ /opt/gbo/work/generalbots/target/debug/botserver \ - ${SYSTEM_USER}@${STAGE_SYSTEM_HOST}:/opt/gbo/bin/botserver-new - + gbuser@system:/opt/gbo/bin/botserver-new scp -i /home/gbuser/.ssh/id_ed25519 -o StrictHostKeyChecking=no \ - /opt/gbo/work/generalbots/target/debug/botui \ - ${SYSTEM_USER}@${STAGE_SYSTEM_HOST}:/opt/gbo/bin/botui-new - - # Restart services on stage + /opt/gbo/work/generalbots/target/release/botui \ + gbuser@system:/opt/gbo/bin/botui-new ssh -i /home/gbuser/.ssh/id_ed25519 -o StrictHostKeyChecking=no \ - ${SYSTEM_USER}@${STAGE_SYSTEM_HOST} "\ - - sudo systemctl stop botserver && sudo systemctl stop botui - sudo killall botui -9 & sudo killall botserver -9 - sudo mv /opt/gbo/bin/botserver-new /opt/gbo/bin/botserver && \ - sudo mv /opt/gbo/bin/botui-new /opt/gbo/bin/botui && \ - sudo chmod +x /opt/gbo/bin/botserver /opt/gbo/bin/botui && \ - sudo systemctl start botserver && \ - sudo systemctl start ui" - + gbuser@system \ + "sudo systemctl stop botserver || true && \ + sudo systemctl stop ui || true && \ + sudo mv /opt/gbo/bin/botserver-new /opt/gbo/bin/botserver && \ + sudo mv /opt/gbo/bin/botui-new /opt/gbo/bin/botui && \ + sudo chmod +x /opt/gbo/bin/botserver /opt/gbo/bin/botui && \ + sudo systemctl start botserver && \ + sudo systemctl start ui" + sleep 10 + ssh -i /home/gbuser/.ssh/id_ed25519 -o StrictHostKeyChecking=no \ + gbuser@system \ + "curl -sf http://localhost:8080/health && echo 'BotServer OK' || echo 'BotServer FAILED'; \ + curl -sf http://localhost:3000/ && echo 'BotUI OK' || echo 'BotUI FAILED'" diff --git a/AGENTS.md b/AGENTS.md index 5b5e3c33..f347f0e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -224,6 +224,11 @@ result = DETECT "folha_salarios" ## 💬 Common BASIC Keywords Reference +### Language Guidelines +- Use formal language in all comments and documentation +- Avoid slang, neologisms, or informal expressions +- Maintain professional tone in code comments + ### TALK - Bot Response ```basic diff --git a/DEV-DEPENDENCIES.sh b/DEV-DEPENDENCIES.sh index 0c840240..287c41d4 100755 --- a/DEV-DEPENDENCIES.sh +++ b/DEV-DEPENDENCIES.sh @@ -1,53 +1,20 @@ #!/bin/bash set -e - -if [ "$EUID" -ne 0 ]; then - echo "Run as root (use sudo)" - exit 1 -fi - +[ "$EUID" -ne 0 ] && { echo "Run as root (use sudo)"; exit 1; } SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -echo "Installing runtime dependencies first..." bash "$SCRIPT_DIR/DEPENDENCIES.sh" - -echo "Installing dev/build dependencies..." OS=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || echo "unknown") - -install_debian() { - apt-get install -y -qq \ - clang lld build-essential pkg-config libssl-dev libpq-dev cmake git \ - libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev \ - libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev -} - -install_fedora() { - dnf install -y -q \ - clang lld gcc gcc-c++ make pkg-config openssl-devel postgresql-devel cmake git \ - glib2-devel gobject-introspection-devel gtk3-devel webkit2gtk3-devel \ - javascriptcoregtk-devel libappindicator-gtk3-devel librsvg2-devel libsoup3-devel -} - -install_arch() { - pacman -Sy --noconfirm \ - clang lld gcc make pkg-config openssl libpq cmake git \ - glib2 gtk3 webkit2gtk4 javascriptcoregtk libappindicator librsvg libsoup -} - +install_debian() { apt-get install -y -qq clang lld build-essential pkg-config libssl-dev libpq-dev cmake git libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev; } +install_fedora() { dnf install -y -q clang lld gcc gcc-c++ make pkg-config openssl-devel postgresql-devel cmake git glib2-devel gobject-introspection-devel gtk3-devel webkit2gtk3-devel javascriptcoregtk-devel libappindicator-gtk3-devel librsvg2-devel libsoup3-devel; } +install_arch() { pacman -Sy --noconfirm clang lld gcc make pkg-config openssl libpq cmake git glib2 gtk3 webkit2gtk4 javascriptcoregtk libappindicator librsvg libsoup; } case $OS in - ubuntu|debian|linuxmint|pop) install_debian ;; - fedora|rhel|centos|rocky|almalinux) install_fedora ;; - arch|manjaro) install_arch ;; - *) echo "Unsupported OS: $OS"; exit 1 ;; + ubuntu|debian|linuxmint|pop) install_debian ;; + fedora|rhel|centos|rocky|almalinux) install_fedora ;; + arch|manjaro) install_arch ;; + *) echo "Unsupported OS: $OS"; exit 1 ;; esac - -install_mold() { - curl -L "https://github.com/rui314/mold/releases/download/v2.4.0/mold-2.4.0-x86_64-linux.tar.gz" -o /tmp/mold.tar.gz - tar -xzf /tmp/mold.tar.gz -C /tmp - cp "/tmp/mold-2.4.0-x86_64-linux/bin/mold" /usr/local/bin/ - rm -rf /tmp/mold-2.4.0* /tmp/mold.tar.gz - ldconfig -} - +install_mold() { curl -L "https://github.com/rui314/mold/releases/download/v2.4.0/mold-2.4.0-x86_64-linux.tar.gz" -o /tmp/mold.tar.gz; tar -xzf /tmp/mold.tar.gz -C /tmp; cp "/tmp/mold-2.4.0-x86_64-linux/bin/mold" /usr/local/bin/; rm -rf /tmp/mold-2.4.0* /tmp/mold.tar.gz; ldconfig; } command -v mold &> /dev/null || install_mold - -echo "Dev dependencies installed!" \ No newline at end of file +echo "✅ Tools: clang, lld, mold, sccache" +echo "📦 Workspace .cargo/config.toml will be used" +echo "⚡ Build: cargo build -p botserver --bin botserver" diff --git a/botmodels/src/anomaly_detection.py b/botmodels/src/anomaly_detection.py index c93bb8fe..2864c147 100644 --- a/botmodels/src/anomaly_detection.py +++ b/botmodels/src/anomaly_detection.py @@ -29,7 +29,10 @@ class AnomalyResult(BaseModel): @app.get("/health") def health(): - return {"status": "healthy", "service": "anomaly-detection"} + return {"status": "healthy", "service": "anomaly-detection", "commit": os.environ.get("BOTMODELS_COMMIT", "unknown")} + + +import os @app.post("/api/detect", response_model=AnomalyResult) diff --git a/botmodels/src/core/config.py b/botmodels/src/core/config.py index cce35d01..ca1fdef6 100644 --- a/botmodels/src/core/config.py +++ b/botmodels/src/core/config.py @@ -20,6 +20,7 @@ class Settings(BaseSettings): project_name: str = "BotModels API" version: str = "2.0.0" api_key: str = "change-me" + commit: str = "unknown" # External Providers for Speech (Optional) groq_api_key: Optional[str] = None diff --git a/botmodels/src/main.py b/botmodels/src/main.py index 1a452fe4..93e4b8c5 100644 --- a/botmodels/src/main.py +++ b/botmodels/src/main.py @@ -62,6 +62,7 @@ async def root(): { "service": settings.project_name, "version": settings.version, + "commit": settings.commit, "status": "running", "docs": "/api/docs", "endpoints": { @@ -78,7 +79,12 @@ async def root(): @app.get("/api/health") async def health(): - return {"status": "healthy", "version": settings.version, "device": settings.device} + return { + "status": "healthy", + "version": settings.version, + "commit": settings.commit, + "device": settings.device, + } if __name__ == "__main__": diff --git a/botserver/build.rs b/botserver/build.rs index 2c80613c..e051effd 100644 --- a/botserver/build.rs +++ b/botserver/build.rs @@ -1,15 +1,48 @@ fn main() { - if std::path::Path::new("../botui/ui/suite/").exists() { - println!("cargo:rerun-if-changed=../botui/ui/suite/"); - } - println!("cargo:rerun-if-changed=3rdparty.toml"); - println!("cargo:rerun-if-changed=.env.embedded"); - - // Pass build metadata to the binary via option_env! - if let Ok(date) = std::env::var("BOTSERVER_BUILD_DATE") { - println!("cargo:rustc-env=BOTSERVER_BUILD_DATE={}", date); - } - if let Ok(commit) = std::env::var("BOTSERVER_COMMIT") { - println!("cargo:rustc-env=BOTSERVER_COMMIT={}", commit); - } +if std::path::Path::new("../botui/ui/suite/").exists() { +println!("cargo:rerun-if-changed=../botui/ui/suite/"); +} +println!("cargo:rerun-if-changed=3rdparty.toml"); +println!("cargo:rerun-if-changed=.env.embedded"); + +if let Ok(date) = std::env::var("BOTSERVER_BUILD_DATE") { +println!("cargo:rustc-env=BOTSERVER_BUILD_DATE={}", date); +} else { +println!("cargo:rustc-env=BOTSERVER_BUILD_DATE={}", chrono_now()); +} + +let commit = std::env::var("BOTSERVER_COMMIT") +.ok() +.or_else(|| git_commit_hash()); +if let Some(hash) = commit { +println!("cargo:rustc-env=BOTSERVER_COMMIT={}", hash); +} +} + +fn git_commit_hash() -> Option { +let output = std::process::Command::new("git") +.args(["rev-parse", "--short", "HEAD"]) +.output() +.ok()?; +if !output.status.success() { +return None; +} +String::from_utf8(output.stdout).ok().map(|s| s.trim().to_string()) +} + +fn chrono_now() -> String { + let output = match std::process::Command::new("date") + .args(["+%Y-%m-%dT%H:%M:%S"]) + .output() + { + Ok(o) => o, + Err(_) => return "unknown".to_string(), + }; + if !output.status.success() { + return "unknown".to_string(); + } + String::from_utf8(output.stdout) + .ok() + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "unknown".to_string()) } diff --git a/botserver/src/core/bootstrap/bootstrap_manager.rs b/botserver/src/core/bootstrap/bootstrap_manager.rs index 91785aea..ff60693d 100644 --- a/botserver/src/core/bootstrap/bootstrap_manager.rs +++ b/botserver/src/core/bootstrap/bootstrap_manager.rs @@ -1,6 +1,6 @@ // Bootstrap manager implementation use crate::core::bootstrap::bootstrap_types::{BootstrapManager, BootstrapProgress}; -use crate::core::bootstrap::bootstrap_utils::{alm_ci_health_check, alm_health_check, cache_health_check, drive_health_check, safe_pkill, tables_health_check, vault_health_check, vector_db_health_check, zitadel_health_check}; +use crate::core::bootstrap::bootstrap_utils::{alm_health_check, cache_health_check, drive_health_check, safe_pkill, tables_health_check, vault_health_check, vector_db_health_check, zitadel_health_check}; use crate::core::config::AppConfig; use crate::core::package_manager::{InstallMode, PackageManager}; use crate::core::shared::utils::get_stack_path; @@ -260,22 +260,23 @@ impl BootstrapManager { } } - if pm.is_installed("alm-ci") { - let alm_ci_already_running = alm_ci_health_check(); - if alm_ci_already_running { - info!("ALM CI (Forgejo Runner) is already running"); - } else { - info!("Starting ALM CI (Forgejo Runner) service..."); - match pm.start("alm-ci") { - Ok(_child) => { - info!("ALM CI service started"); - } - Err(e) => { - warn!("Failed to start ALM CI service: {}", e); - } - } - } - } + // TEMP DISABLED: ALM CI startup hangs bootstrap + // if pm.is_installed("alm-ci") { + // let alm_ci_already_running = alm_ci_health_check(); + // if alm_ci_already_running { + // info!("ALM CI (Forgejo Runner) is already running"); + // } else { + // info!("Starting ALM CI (Forgejo Runner) service..."); + // match pm.start("alm-ci") { + // Ok(_child) => { + // info!("ALM CI service started"); + // } + // Err(e) => { + // warn!("Failed to start ALM CI service: {}", e); + // } + // } + // } + // } // Caddy is the web server let caddy_cmd = SafeCommand::new("caddy") diff --git a/botserver/src/core/package_manager/installer.rs b/botserver/src/core/package_manager/installer.rs index 0a3ebb06..d4c4e644 100644 --- a/botserver/src/core/package_manager/installer.rs +++ b/botserver/src/core/package_manager/installer.rs @@ -1693,18 +1693,21 @@ VAULT_CACERT={} ("smtp_from".to_string(), "none".to_string()), ], ), - ( - "secret/gbo/llm", - vec![ - ("url".to_string(), "".to_string()), - ("host".to_string(), "localhost".to_string()), - ("port".to_string(), "8081".to_string()), - ("model".to_string(), "gpt-4".to_string()), - ("openai_key".to_string(), "none".to_string()), - ("anthropic_key".to_string(), "none".to_string()), - ("ollama_url".to_string(), "".to_string()), - ], - ), + ( + "secret/gbo/llm", + vec![ + ("url".to_string(), "".to_string()), + ("host".to_string(), "localhost".to_string()), + ("port".to_string(), "8081".to_string()), + ("model".to_string(), "gpt-4".to_string()), + ("openai_key".to_string(), "none".to_string()), + ("anthropic_key".to_string(), "none".to_string()), + ("ollama_url".to_string(), "".to_string()), + ("embedding_url".to_string(), "http://localhost:8082/v1/embeddings".to_string()), + ("embedding_model".to_string(), "bge-small-en-v1.5-f32.gguf".to_string()), + ("embedding_port".to_string(), "8082".to_string()), + ], + ), ( "secret/gbo/encryption", vec![("master_key".to_string(), master_key)], diff --git a/botserver/src/llm/local.rs b/botserver/src/llm/local.rs index 4546130b..9aa1ba73 100644 --- a/botserver/src/llm/local.rs +++ b/botserver/src/llm/local.rs @@ -61,7 +61,7 @@ pub async fn ensure_llama_servers_running( ) }; let ( - _default_bot_id, + default_bot_id, llm_server_enabled, llm_url, llm_model, @@ -79,19 +79,44 @@ pub async fn ensure_llama_servers_running( llm_server_path }; - let llm_model = if llm_model.is_empty() { - info!("No LLM model configured, using default: DeepSeek-R1-Distill-Qwen-1.5B-Q3_K_M.gguf"); - "DeepSeek-R1-Distill-Qwen-1.5B-Q3_K_M.gguf".to_string() - } else { - llm_model - }; +let llm_url = if llm_url.is_empty() && llm_server_enabled { + let url = "http://localhost:8081/v1/chat/completions".to_string(); + info!("No llm-url configured with local server enabled, using default: {url}"); + let config_manager = ConfigManager::new(app_state.conn.clone()); + if let Err(e) = config_manager.set_config(&default_bot_id, "llm-url", &url) { + warn!("Failed to persist default llm-url: {e}"); + } + url +} else { + llm_url +}; - let embedding_model = if embedding_model.is_empty() { - info!("No embedding model configured, using default: bge-small-en-v1.5-f32.gguf"); - "bge-small-en-v1.5-f32.gguf".to_string() - } else { - embedding_model - }; +let llm_model = if llm_model.is_empty() { + info!("No LLM model configured, using default: DeepSeek-R1-Distill-Qwen-1.5B-Q3_K_M.gguf"); + "DeepSeek-R1-Distill-Qwen-1.5B-Q3_K_M.gguf".to_string() +} else { + llm_model +}; + +let embedding_model = if embedding_model.is_empty() { + info!("No embedding model configured, using default: bge-small-en-v1.5-f32.gguf"); + "bge-small-en-v1.5-f32.gguf".to_string() +} else { + embedding_model +}; + +let embedding_url = if embedding_url.is_empty() { + let default_port = "8082"; + let url = format!("http://localhost:{default_port}/v1/embeddings"); + info!("No embedding-url configured, using default: {url}"); + let config_manager = ConfigManager::new(app_state.conn.clone()); + if let Err(e) = config_manager.set_config(&default_bot_id, "embedding-url", &url) { + warn!("Failed to persist default embedding-url: {e}"); + } + url +} else { + embedding_url +}; // For llama-server startup, use path relative to botserver root // The models are in /data/llm/ and the llama-server runs from botserver root @@ -443,7 +468,7 @@ pub fn start_llm_server( .unwrap_or_else(|_| "32000".to_string()); let n_ctx_size = if n_ctx_size.is_empty() { "32000".to_string() } else { n_ctx_size }; - let _cmd_path = if cfg!(windows) { + let cmd_path = if cfg!(windows) { format!("{}\\llama-server.exe", llama_cpp_path) } else { format!("{}/llama-server", llama_cpp_path) @@ -489,12 +514,10 @@ pub fn start_llm_server( args_vec.push(&n_ctx_size); args_vec.push("--verbose"); - let mut command = SafeCommand::new("llama-server")?; + let mut command = SafeCommand::new(&cmd_path)?; command = command.args(&args_vec)?; - - if cfg!(windows) { - command = command.working_dir(std::path::Path::new(&llama_cpp_path))?; - } + command = command.working_dir(std::path::Path::new(&llama_cpp_path))?; + command = command.env("LD_LIBRARY_PATH", &llama_cpp_path)?; let log_file_path = if cfg!(windows) { format!("{}\\llm-stdout.log", llama_cpp_path) @@ -558,22 +581,27 @@ pub async fn start_embedding_server( "-m", &model_path, "--host", "0.0.0.0", "--port", port, - "--embedding", + "--embeddings", + "--pooling", "mean", "--n-gpu-layers", "0", - "--verbose", + "--ctx-size", "512", ]; if !cfg!(windows) { args_vec.push("--ubatch-size"); - args_vec.push("2048"); + args_vec.push("512"); } - let mut command = SafeCommand::new("llama-server")?; + let cmd_path = if cfg!(windows) { + format!("{}\\llama-server.exe", llama_cpp_path) + } else { + format!("{}/llama-server", llama_cpp_path) + }; + + let mut command = SafeCommand::new(&cmd_path)?; command = command.args(&args_vec)?; - - if cfg!(windows) { - command = command.working_dir(std::path::Path::new(&llama_cpp_path))?; - } + command = command.working_dir(std::path::Path::new(&llama_cpp_path))?; + command = command.env("LD_LIBRARY_PATH", &llama_cpp_path)?; let log_file_path = if cfg!(windows) { format!("{}\\stdout.log", llama_cpp_path) diff --git a/botserver/src/main.rs b/botserver/src/main.rs index f33f38c8..63bcbe65 100644 --- a/botserver/src/main.rs +++ b/botserver/src/main.rs @@ -1,7 +1,7 @@ #![recursion_limit = "512"] // Module declarations -pub mod main_module; +pub mod main_module; // ci-timing // Re-export commonly used items from main_module pub use main_module::{BootstrapProgress, health_check, health_check_simple, receive_client_errors}; @@ -255,6 +255,7 @@ rustls=off,rustls_pemfile=off,tokio_rustls=off,\ Ok(existing) if !existing.is_empty() => format!("{},{}", existing, noise_filters), _ => format!("info,{}", noise_filters), }; +// Test mold+sccache build std::env::set_var("RUST_LOG", &rust_log); @@ -433,3 +434,4 @@ rustls=off,rustls_pemfile=off,tokio_rustls=off,\ Ok(()) } // force rebuild Fri Apr 3 21:42:33 -03 2026 +// Force new CI run - Tue Apr 28 05:22:39 PM -03 2026 diff --git a/botui/build.rs b/botui/build.rs index f05cdb8a..abd286c7 100644 --- a/botui/build.rs +++ b/botui/build.rs @@ -1,5 +1,23 @@ fn main() { - let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - let ui_path = std::path::Path::new(&manifest_dir).join("ui"); - println!("cargo:rustc-env=BOTUI_UI_PATH={}", ui_path.display()); +let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); +let ui_path = std::path::Path::new(&manifest_dir).join("ui"); +println!("cargo:rustc-env=BOTUI_UI_PATH={}", ui_path.display()); + +let commit = std::env::var("BOTUI_COMMIT") +.ok() +.or_else(|| git_commit_hash()); +if let Some(hash) = commit { +println!("cargo:rustc-env=BOTUI_COMMIT={}", hash); +} +} + +fn git_commit_hash() -> Option { +let output = std::process::Command::new("git") +.args(["rev-parse", "--short", "HEAD"]) +.output() +.ok()?; +if !output.status.success() { +return None; +} +String::from_utf8(output.stdout).ok().map(|s| s.trim().to_string()) } \ No newline at end of file diff --git a/botui/src/ui_server/mod.rs b/botui/src/ui_server/mod.rs index 89e4232f..56c8e7ef 100644 --- a/botui/src/ui_server/mod.rs +++ b/botui/src/ui_server/mod.rs @@ -595,13 +595,16 @@ pub fn remove_section(html: &str, section: &str) -> String { } async fn health(State(state): State) -> (StatusCode, axum::Json) { + let commit = option_env!("BOTUI_COMMIT").unwrap_or("unknown"); if state.health_check().await { ( StatusCode::OK, axum::Json(serde_json::json!({ "status": "healthy", "service": "botui", - "mode": "web" + "mode": "web", + "version": env!("CARGO_PKG_VERSION"), + "commit": commit })), ) } else { @@ -610,20 +613,24 @@ async fn health(State(state): State) -> (StatusCode, axum::Json) -> (StatusCode, axum::Json) { + let commit = option_env!("BOTUI_COMMIT").unwrap_or("unknown"); if state.health_check().await { ( StatusCode::OK, axum::Json(serde_json::json!({ "status": "ok", "botserver": "healthy", - "version": env!("CARGO_PKG_VERSION") + "version": env!("CARGO_PKG_VERSION"), + "commit": commit })), ) } else { @@ -632,7 +639,8 @@ async fn api_health(State(state): State) -> (StatusCode, axum::Json