refactor(automation, llm): simplify service init and prompt handling

- Remove `scripts_dir` from `AutomationService` and its constructor, as it was unused.
- Drop LLM and embedding server readiness checks; the service now only schedules periodic tasks.
- Increase the health‑check interval from 5 seconds to 15 seconds for reduced load.
- Streamline the LLM keyword prompt to a concise `"User: {}"` format, removing verbose boilerplate.
- Remove unnecessary logging and LLM cache handling code from the bot orchestrator, cleaning up unused environment variable checks and cache queries.
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-11-03 08:23:03 -03:00
parent a5bfda4d09
commit b033be3e64
10 changed files with 37 additions and 154 deletions

View file

@ -12,19 +12,13 @@ use tokio::time::Duration;
use uuid::Uuid; use uuid::Uuid;
pub struct AutomationService { pub struct AutomationService {
state: Arc<AppState>, state: Arc<AppState>
scripts_dir: String,
} }
impl AutomationService { impl AutomationService {
pub fn new(state: Arc<AppState>, scripts_dir: &str) -> Self { pub fn new(state: Arc<AppState>) -> Self {
trace!(
"Creating AutomationService with scripts_dir='{}'",
scripts_dir
);
Self { Self {
state, state
scripts_dir: scripts_dir.to_string(),
} }
} }
@ -34,39 +28,8 @@ impl AutomationService {
tokio::task::spawn_local({ tokio::task::spawn_local({
let service = service.clone(); let service = service.clone();
async move { async move {
// Check if llama servers are ready before starting
let config_manager = ConfigManager::new(Arc::clone(&service.state.conn));
let default_bot_id = {
let mut conn = service.state.conn.lock().unwrap();
bots.filter(name.eq("default"))
.select(id)
.first::<uuid::Uuid>(&mut *conn)
.unwrap_or_else(|_| uuid::Uuid::nil())
};
let llm_url = match config_manager.get_config(&default_bot_id, "llm-url", None) { let mut interval = tokio::time::interval(Duration::from_secs(15));
Ok(url) => url,
Err(e) => {
error!("Failed to get llm-url config: {}", e);
return;
}
};
let embedding_url = match config_manager.get_config(&default_bot_id, "embedding-url", None) {
Ok(url) => url,
Err(e) => {
error!("Failed to get embedding-url config: {}", e);
return;
}
};
if !crate::llm::local::is_server_running(&llm_url).await ||
!crate::llm::local::is_server_running(&embedding_url).await {
trace!("LLM servers not ready - llm: {}, embedding: {}", llm_url, embedding_url);
return;
}
let mut interval = tokio::time::interval(Duration::from_secs(5));
let mut last_check = Utc::now(); let mut last_check = Utc::now();
loop { loop {
interval.tick().await; interval.tick().await;

View file

@ -70,14 +70,7 @@ pub fn llm_keyword(state: Arc<AppState>, _user: UserSession, engine: &mut Engine
/// You can change the style/structure here to guide the model's behavior. /// You can change the style/structure here to guide the model's behavior.
fn build_llm_prompt(user_text: &str) -> String { fn build_llm_prompt(user_text: &str) -> String {
format!( format!(
"You are a AI assistant in form of KEYWORD called LLM "User: {}",
running inside a General Bots BASIC environment.
Task: Process and respond concisely to the following call to x = LLM 'prompt' syntax.
---
User Input:
{}
---
Respond clearly and accurately in the same language as the input.",
user_text.trim() user_text.trim()
) )
} }
@ -87,7 +80,6 @@ pub async fn execute_llm_generation(
state: Arc<AppState>, state: Arc<AppState>,
prompt: String, prompt: String,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
info!("Starting LLM generation for prompt: '{}'", prompt);
state state
.llm_provider .llm_provider

View file

@ -569,94 +569,23 @@ impl BotOrchestrator {
prompt.push_str(&format!("User: {}\nAssistant:", message.content)); prompt.push_str(&format!("User: {}\nAssistant:", message.content));
let use_langcache = std::env::var("LLM_CACHE")
.unwrap_or_else(|_| "false".to_string())
.eq_ignore_ascii_case("true");
if use_langcache { let (tx, mut rx) = mpsc::channel::<String>(100); let llm = self.state.llm_provider.clone();
ensure_collection_exists(&self.state, "semantic_cache").await?; tokio::spawn(async move {
let langcache_client = get_langcache_client()?; if let Err(e) = llm
let isolated_question = message.content.trim().to_string(); .generate_stream(&prompt, &serde_json::Value::Null, tx)
let question_embeddings = generate_embeddings(vec![isolated_question.clone()]).await?; .await
let question_embedding = question_embeddings {
.get(0) error!("LLM streaming error in direct_mode_handler: {}", e);
.ok_or_else(|| "Failed to generate embedding for question")?
.clone();
let search_results = langcache_client
.search("semantic_cache", question_embedding.clone(), 1)
.await?;
if let Some(result) = search_results.first() {
let payload = &result.payload;
if let Some(resp) = payload.get("response").and_then(|v| v.as_str()) {
return Ok(resp.to_string());
}
} }
});
let response = self let mut full_response = String::new();
.state while let Some(chunk) = rx.recv().await {
.llm_provider full_response.push_str(&chunk);
.generate(&prompt, &serde_json::Value::Null)
.await?;
let point = QdrantPoint {
id: uuid::Uuid::new_v4().to_string(),
vector: question_embedding,
payload: serde_json::json!({
"question": isolated_question,
"prompt": prompt,
"response": response
}),
};
langcache_client
.upsert_points("semantic_cache", vec![point])
.await?;
Ok(response)
} else {
ensure_collection_exists(&self.state, "semantic_cache").await?;
let qdrant_client = get_qdrant_client(&self.state)?;
let embeddings = generate_embeddings(vec![prompt.clone()]).await?;
let embedding = embeddings
.get(0)
.ok_or_else(|| "Failed to generate embedding")?
.clone();
let search_results = qdrant_client
.search("semantic_cache", embedding.clone(), 1)
.await?;
if let Some(result) = search_results.first() {
if let Some(payload) = &result.payload {
if let Some(resp) = payload.get("response").and_then(|v| v.as_str()) {
return Ok(resp.to_string());
}
}
}
let response = self
.state
.llm_provider
.generate(&prompt, &serde_json::Value::Null)
.await?;
let point = QdrantPoint {
id: uuid::Uuid::new_v4().to_string(),
vector: embedding,
payload: serde_json::json!({
"prompt": prompt,
"response": response
}),
};
qdrant_client
.upsert_points("semantic_cache", vec![point])
.await?;
Ok(response)
} }
Ok(full_response)
} }
pub async fn stream_response( pub async fn stream_response(

View file

@ -244,19 +244,6 @@ async fn main() -> std::io::Result<()> {
.map(|n| n.get()) .map(|n| n.get())
.unwrap_or(4); .unwrap_or(4);
let automation_state = app_state.clone();
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Failed to create runtime for automation");
let local = tokio::task::LocalSet::new();
local.block_on(&rt, async move {
let scripts_dir = "work/default.gbai/.gbdialog".to_string();
let automation = AutomationService::new(automation_state, &scripts_dir);
automation.spawn().await.ok();
});
});
// Initialize bot orchestrator and mount all bots // Initialize bot orchestrator and mount all bots
let bot_orchestrator = BotOrchestrator::new(app_state.clone()); let bot_orchestrator = BotOrchestrator::new(app_state.clone());
@ -271,6 +258,18 @@ async fn main() -> std::io::Result<()> {
.await .await
.expect("Failed to initialize LLM local server"); .expect("Failed to initialize LLM local server");
let automation_state = app_state.clone();
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Failed to create runtime for automation");
let local = tokio::task::LocalSet::new();
local.block_on(&rt, async move {
let automation = AutomationService::new(automation_state);
automation.spawn().await.ok();
});
});
HttpServer::new(move || { HttpServer::new(move || {

View file

@ -293,9 +293,9 @@ impl SessionManager {
let mut history: Vec<(String, String)> = Vec::new(); let mut history: Vec<(String, String)> = Vec::new();
for (other_role, content) in messages { for (other_role, content) in messages {
let role_str = match other_role { let role_str = match other_role {
0 => "user".to_string(), 1 => "user".to_string(),
1 => "assistant".to_string(), 2 => "assistant".to_string(),
2 => "system".to_string(), 3 => "system".to_string(),
_ => "unknown".to_string(), _ => "unknown".to_string(),
}; };
history.push((role_str, content)); history.push((role_str, content));

View file

@ -1,4 +1,4 @@
let resume1 = GET_BOT_MEMORY("general"); let resume1 = GET_BOT_MEMORY("resume");
let resume2 = GET_BOT_MEMORY("auxiliom"); let resume2 = GET_BOT_MEMORY("auxiliom");
let resume3 = GET_BOT_MEMORY("toolbix"); let resume3 = GET_BOT_MEMORY("toolbix");
@ -14,5 +14,5 @@ ADD_SUGGESTION "auxiliom" AS "What does Auxiliom do?"
ADD_SUGGESTION "toolbix" AS "Show me Toolbix features" ADD_SUGGESTION "toolbix" AS "Show me Toolbix features"
ADD_SUGGESTION "toolbix" AS "How can Toolbix help my business?" ADD_SUGGESTION "toolbix" AS "How can Toolbix help my business?"
TALK resume1
TALK "You can ask me about any of the announcements or circulars." TALK "You can ask me about any of the announcements or circulars."

View file

@ -1,8 +1,8 @@
SET_SCHEDULE "* * * * *" SET_SCHEDULE "*/10 * * * *"
let text = GET "announcements.gbkb/news/news.pdf" let text = GET "announcements.gbkb/news/news.pdf"
let resume = LLM "Resume this document, in a table (DO NOT THINK) no_think: " + text let resume = LLM "Resume this document: " + text
SET_BOT_MEMORY "resume", resume SET_BOT_MEMORY "resume", resume