fix: use compile_preprocessed for .ast files
All checks were successful
BotServer CI/CD / build (push) Successful in 3m29s
All checks were successful
BotServer CI/CD / build (push) Successful in 3m29s
This commit is contained in:
parent
af85426ed4
commit
20af25e9e2
2 changed files with 123 additions and 101 deletions
|
|
@ -580,6 +580,15 @@ impl ScriptService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compile preprocessed script content (from .ast file) - skips preprocessing
|
||||||
|
pub fn compile_preprocessed(&self, script: &str) -> Result<rhai::AST, Box<EvalAltResult>> {
|
||||||
|
trace!("Compiling preprocessed script directly");
|
||||||
|
match self.engine.compile(script) {
|
||||||
|
Ok(ast) => Ok(ast),
|
||||||
|
Err(parse_error) => Err(Box::new(parse_error.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Compile a tool script (.bas file with PARAM/DESCRIPTION metadata lines)
|
/// Compile a tool script (.bas file with PARAM/DESCRIPTION metadata lines)
|
||||||
/// Filters out tool metadata before compiling
|
/// Filters out tool metadata before compiling
|
||||||
pub fn compile_tool_script(&self, script: &str) -> Result<rhai::AST, Box<EvalAltResult>> {
|
pub fn compile_tool_script(&self, script: &str) -> Result<rhai::AST, Box<EvalAltResult>> {
|
||||||
|
|
|
||||||
|
|
@ -618,82 +618,88 @@ impl BotOrchestrator {
|
||||||
true // If no cache, try to execute
|
true // If no cache, try to execute
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_execute_start_bas {
|
if should_execute_start_bas {
|
||||||
// Execute start.bas from work directory
|
// Execute start.bas from work directory
|
||||||
let work_path = crate::core::shared::utils::get_work_path();
|
let work_path = crate::core::shared::utils::get_work_path();
|
||||||
let start_script_path = format!("{}/{}.gbai/{}.gbdialog/start.bas", work_path, bot_name_for_context, bot_name_for_context);
|
let start_script_path = format!("{}/{}.gbai/{}.gbdialog/start.bas", work_path, bot_name_for_context, bot_name_for_context);
|
||||||
|
|
||||||
trace!("Executing start.bas for session {} at: {}", actual_session_id, start_script_path);
|
trace!("Executing start.bas for session {} at: {}", actual_session_id, start_script_path);
|
||||||
|
|
||||||
// Use pre-compiled .ast if available (avoids recompilation)
|
// Use pre-compiled .ast if available (avoids preprocessing)
|
||||||
let ast_path = start_script_path.replace(".bas", ".ast");
|
let ast_path = start_script_path.replace(".bas", ".ast");
|
||||||
let script_content = if std::path::Path::new(&ast_path).exists() {
|
let (script_content, is_preprocessed) = if std::path::Path::new(&ast_path).exists() {
|
||||||
tokio::fs::read_to_string(&ast_path).await.unwrap_or_default()
|
(tokio::fs::read_to_string(&ast_path).await.unwrap_or_default(), true)
|
||||||
} else {
|
} else {
|
||||||
tokio::fs::read_to_string(&start_script_path).await.unwrap_or_default()
|
(tokio::fs::read_to_string(&start_script_path).await.unwrap_or_default(), false)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !script_content.is_empty() {
|
if !script_content.is_empty() {
|
||||||
let state_clone = self.state.clone();
|
let state_clone = self.state.clone();
|
||||||
let actual_session_id_for_task = session.id;
|
let actual_session_id_for_task = session.id;
|
||||||
let bot_id_clone = session.bot_id;
|
let bot_id_clone = session.bot_id;
|
||||||
|
|
||||||
// Execute start.bas synchronously (blocking)
|
// Execute start.bas synchronously (blocking)
|
||||||
let result = tokio::task::spawn_blocking(move || {
|
let result = tokio::task::spawn_blocking(move || {
|
||||||
let session_result = {
|
let session_result = {
|
||||||
let mut sm = state_clone.session_manager.blocking_lock();
|
let mut sm = state_clone.session_manager.blocking_lock();
|
||||||
sm.get_session_by_id(actual_session_id_for_task)
|
sm.get_session_by_id(actual_session_id_for_task)
|
||||||
};
|
};
|
||||||
|
|
||||||
let sess = match session_result {
|
let sess = match session_result {
|
||||||
Ok(Some(s)) => s,
|
Ok(Some(s)) => s,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
return Err(format!("Session {} not found during start.bas execution", actual_session_id_for_task));
|
return Err(format!("Session {} not found during start.bas execution", actual_session_id_for_task));
|
||||||
}
|
|
||||||
Err(e) => return Err(format!("Failed to get session: {}", e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut script_service = crate::basic::ScriptService::new(
|
|
||||||
state_clone.clone(),
|
|
||||||
sess
|
|
||||||
);
|
|
||||||
script_service.load_bot_config_params(&state_clone, bot_id_clone);
|
|
||||||
|
|
||||||
match script_service.compile(&script_content) {
|
|
||||||
Ok(ast) => match script_service.run(&ast) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(e) => Err(format!("Script execution error: {}", e)),
|
|
||||||
},
|
|
||||||
Err(e) => Err(format!("Script compilation error: {}", e)),
|
|
||||||
}
|
}
|
||||||
}).await;
|
Err(e) => return Err(format!("Failed to get session: {}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
match result {
|
let mut script_service = crate::basic::ScriptService::new(
|
||||||
Ok(Ok(())) => {
|
state_clone.clone(),
|
||||||
trace!("start.bas completed successfully for session {}", actual_session_id);
|
sess
|
||||||
|
);
|
||||||
|
script_service.load_bot_config_params(&state_clone, bot_id_clone);
|
||||||
|
|
||||||
// Mark start.bas as executed for this session to prevent re-running
|
let compile_result = if is_preprocessed {
|
||||||
if let Some(cache) = &self.state.cache {
|
script_service.compile_preprocessed(&script_content)
|
||||||
if let Ok(mut conn) = cache.get_multiplexed_async_connection().await {
|
} else {
|
||||||
let _: Result<(), redis::RedisError> = redis::cmd("SET")
|
script_service.compile(&script_content)
|
||||||
.arg(&start_bas_key)
|
};
|
||||||
.arg("1")
|
|
||||||
.arg("EX")
|
match compile_result {
|
||||||
.arg("86400") // Expire after 24 hours
|
Ok(ast) => match script_service.run(&ast) {
|
||||||
.query_async(&mut conn)
|
Ok(_) => Ok(()),
|
||||||
.await;
|
Err(e) => Err(format!("Script execution error: {}", e)),
|
||||||
}
|
},
|
||||||
}
|
Err(e) => Err(format!("Script compilation error: {}", e)),
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
}).await;
|
||||||
error!("start.bas error for session {}: {}", actual_session_id, e);
|
|
||||||
}
|
match result {
|
||||||
|
Ok(Ok(())) => {
|
||||||
|
trace!("start.bas completed successfully for session {}", actual_session_id);
|
||||||
|
|
||||||
|
// Mark start.bas as executed for this session to prevent re-running
|
||||||
|
if let Some(cache) = &self.state.cache {
|
||||||
|
if let Ok(mut conn) = cache.get_multiplexed_async_connection().await {
|
||||||
|
let _: Result<(), redis::RedisError> = redis::cmd("SET")
|
||||||
|
.arg(&start_bas_key)
|
||||||
|
.arg("1")
|
||||||
|
.arg("EX")
|
||||||
|
.arg("86400") // Expire after 24 hours
|
||||||
|
.query_async(&mut conn)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
error!("start.bas error for session {}: {}", actual_session_id, e);
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("start.bas task error for session {}: {}", actual_session_id, e);
|
error!("start.bas task error for session {}: {}", actual_session_id, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // End of if should_execute_start_bas
|
} // End of if should_execute_start_bas
|
||||||
|
|
||||||
// If message content is empty, we stop here after potentially running start.bas.
|
// If message content is empty, we stop here after potentially running start.bas.
|
||||||
// This happens when the bot is activated by its name in WhatsApp, where an empty string is sent as a signal.
|
// This happens when the bot is activated by its name in WhatsApp, where an empty string is sent as a signal.
|
||||||
|
|
@ -1408,25 +1414,25 @@ async fn handle_websocket(
|
||||||
|
|
||||||
info!("Looking for start.bas at: {}", start_script_path);
|
info!("Looking for start.bas at: {}", start_script_path);
|
||||||
|
|
||||||
// Check for pre-compiled .ast file first (avoids recompilation overhead)
|
// Check for pre-compiled .ast file first (avoids preprocessing overhead)
|
||||||
let ast_path = start_script_path.replace(".bas", ".ast");
|
let ast_path = start_script_path.replace(".bas", ".ast");
|
||||||
let (script_content, _using_ast) = if tokio::fs::metadata(&ast_path).await.is_ok() {
|
let (script_content, is_preprocessed) = if tokio::fs::metadata(&ast_path).await.is_ok() {
|
||||||
if let Ok(content) = tokio::fs::read_to_string(&ast_path).await {
|
if let Ok(content) = tokio::fs::read_to_string(&ast_path).await {
|
||||||
info!("Using pre-compiled start.ast for {}", bot_name);
|
info!("Using pre-compiled start.ast for {}", bot_name);
|
||||||
(content, true)
|
(content, true)
|
||||||
} else {
|
|
||||||
(String::new(), false)
|
|
||||||
}
|
|
||||||
} else if tokio::fs::metadata(&start_script_path).await.is_ok() {
|
|
||||||
if let Ok(content) = tokio::fs::read_to_string(&start_script_path).await {
|
|
||||||
info!("Compiling start.bas for {}", bot_name);
|
|
||||||
(content, false)
|
|
||||||
} else {
|
|
||||||
(String::new(), false)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
(String::new(), false)
|
(String::new(), false)
|
||||||
};
|
}
|
||||||
|
} else if tokio::fs::metadata(&start_script_path).await.is_ok() {
|
||||||
|
if let Ok(content) = tokio::fs::read_to_string(&start_script_path).await {
|
||||||
|
info!("Compiling start.bas for {}", bot_name);
|
||||||
|
(content, false)
|
||||||
|
} else {
|
||||||
|
(String::new(), false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(String::new(), false)
|
||||||
|
};
|
||||||
|
|
||||||
if !script_content.is_empty() {
|
if !script_content.is_empty() {
|
||||||
info!(
|
info!(
|
||||||
|
|
@ -1434,12 +1440,13 @@ async fn handle_websocket(
|
||||||
bot_name, session_id
|
bot_name, session_id
|
||||||
);
|
);
|
||||||
|
|
||||||
let state_for_start = state.clone();
|
let state_for_start = state.clone();
|
||||||
let tx_for_start = tx.clone();
|
let tx_for_start = tx.clone();
|
||||||
let bot_id_str = bot_id.to_string();
|
let bot_id_str = bot_id.to_string();
|
||||||
let session_id_str = session_id.to_string();
|
let session_id_str = session_id.to_string();
|
||||||
let mut send_ready_rx = send_ready_rx;
|
let mut send_ready_rx = send_ready_rx;
|
||||||
let script_content_owned = script_content.clone();
|
let script_content_owned = script_content.clone();
|
||||||
|
let is_preprocessed_owned = is_preprocessed;
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _ = send_ready_rx.recv().await;
|
let _ = send_ready_rx.recv().await;
|
||||||
|
|
@ -1468,22 +1475,28 @@ async fn handle_websocket(
|
||||||
// Clone state_for_start for use in Redis SET after execution
|
// Clone state_for_start for use in Redis SET after execution
|
||||||
let state_for_redis = state_for_start.clone();
|
let state_for_redis = state_for_start.clone();
|
||||||
|
|
||||||
let result = tokio::task::spawn_blocking(move || {
|
let result = tokio::task::spawn_blocking(move || {
|
||||||
info!("start.bas: Creating ScriptService with session.id={}", session.id);
|
info!("start.bas: Creating ScriptService with session.id={}", session.id);
|
||||||
let mut script_service = crate::basic::ScriptService::new(
|
let mut script_service = crate::basic::ScriptService::new(
|
||||||
state_for_start.clone(),
|
state_for_start.clone(),
|
||||||
session.clone()
|
session.clone()
|
||||||
);
|
);
|
||||||
script_service.load_bot_config_params(&state_for_start, bot_id);
|
script_service.load_bot_config_params(&state_for_start, bot_id);
|
||||||
|
|
||||||
match script_service.compile(&script_content_owned) {
|
let compile_result = if is_preprocessed_owned {
|
||||||
Ok(ast) => match script_service.run(&ast) {
|
script_service.compile_preprocessed(&script_content_owned)
|
||||||
Ok(_) => Ok(()),
|
} else {
|
||||||
Err(e) => Err(format!("Script execution error: {}", e)),
|
script_service.compile(&script_content_owned)
|
||||||
},
|
};
|
||||||
Err(e) => Err(format!("Script compilation error: {}", e)),
|
|
||||||
}
|
match compile_result {
|
||||||
}).await;
|
Ok(ast) => match script_service.run(&ast) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(format!("Script execution error: {}", e)),
|
||||||
|
},
|
||||||
|
Err(e) => Err(format!("Script compilation error: {}", e)),
|
||||||
|
}
|
||||||
|
}).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok(())) => {
|
Ok(Ok(())) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue