fix: Embedding server auto-config and KB indexing fixes
- botserver/src/llm/local.rs: * Auto-configure embedding-url when empty (http://localhost:8082/v1/embeddings) * Auto-configure llm-url when empty (http://localhost:8081/v1/chat/completions) * Persist defaults to bot_configuration table via ConfigManager * Fix llama-server command path and add LD_LIBRARY_PATH * Fix working_dir for Linux (not just Windows) * Fix embedding server args: --embeddings --pooling mean --ctx-size 512 - botserver/src/core/bootstrap/bootstrap_manager.rs: * Temp disable alm-ci startup (causes 20s hang in bootstrap) * Remove unused alm_ci_health_check import - Database changes: * bot_configuration.embedding-model: 'bge-small-en-v1.5-f32.gguf' (full filename) * bot_configuration.embedding-url: 'http://localhost:8082/v1/embeddings' (auto-generated) - Testing: * Embedding server responds on port 8082 * Generates 384-dimension embeddings * KB file exists: minio://default.gbai/default.gbkb/manual/test-kb-manual.txt * Next: Verify KB indexing and search functionality Refs: #498
This commit is contained in:
parent
18396aa316
commit
1a36f4aed2
9 changed files with 179 additions and 78 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__":
|
||||
|
|
|
|||
|
|
@ -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<String> {
|
||||
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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)],
|
||||
|
|
|
|||
|
|
@ -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 <stack_path>/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)
|
||||
|
|
|
|||
|
|
@ -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<String> {
|
||||
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())
|
||||
}
|
||||
|
|
@ -595,13 +595,16 @@ pub fn remove_section(html: &str, section: &str) -> String {
|
|||
}
|
||||
|
||||
async fn health(State(state): State<AppState>) -> (StatusCode, axum::Json<serde_json::Value>) {
|
||||
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<AppState>) -> (StatusCode, axum::Json<serde_
|
|||
axum::Json(serde_json::json!({
|
||||
"status": "unhealthy",
|
||||
"service": "botui",
|
||||
"error": "botserver unreachable"
|
||||
"error": "botserver unreachable",
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
"commit": commit
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async fn api_health(State(state): State<AppState>) -> (StatusCode, axum::Json<serde_json::Value>) {
|
||||
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<AppState>) -> (StatusCode, axum::Json<se
|
|||
axum::Json(serde_json::json!({
|
||||
"status": "error",
|
||||
"botserver": "unhealthy",
|
||||
"version": env!("CARGO_PKG_VERSION")
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
"commit": commit
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue