2025-11-21 09:28:35 -03:00
|
|
|
use crate::shared::models::UserSession;
|
|
|
|
|
use crate::shared::state::AppState;
|
|
|
|
|
use diesel::prelude::*;
|
|
|
|
|
use log::{error, info, warn};
|
2025-11-21 23:23:53 -03:00
|
|
|
use rhai::{Dynamic, Engine, EvalAltResult};
|
2025-11-21 09:28:35 -03:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
2025-11-21 23:23:53 -03:00
|
|
|
#[derive(QueryableByName)]
|
|
|
|
|
struct BotNameResult {
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Text)]
|
|
|
|
|
name: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(QueryableByName)]
|
|
|
|
|
struct KbCollectionResult {
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Text)]
|
|
|
|
|
folder_path: String,
|
|
|
|
|
#[diesel(sql_type = diesel::sql_types::Text)]
|
|
|
|
|
qdrant_collection: String,
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-22 12:26:16 -03:00
|
|
|
#[derive(QueryableByName, Debug, Clone)]
|
|
|
|
|
pub struct ActiveKbResult {
|
2025-11-21 23:23:53 -03:00
|
|
|
#[diesel(sql_type = diesel::sql_types::Text)]
|
2025-11-22 12:26:16 -03:00
|
|
|
pub kb_name: String,
|
2025-11-21 23:23:53 -03:00
|
|
|
#[diesel(sql_type = diesel::sql_types::Text)]
|
2025-11-22 12:26:16 -03:00
|
|
|
pub kb_folder_path: String,
|
2025-11-21 23:23:53 -03:00
|
|
|
#[diesel(sql_type = diesel::sql_types::Text)]
|
2025-11-22 12:26:16 -03:00
|
|
|
pub qdrant_collection: String,
|
2025-11-21 23:23:53 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn register_use_kb_keyword(
|
2025-11-21 09:28:35 -03:00
|
|
|
engine: &mut Engine,
|
|
|
|
|
state: Arc<AppState>,
|
|
|
|
|
session: Arc<UserSession>,
|
|
|
|
|
) -> Result<(), Box<EvalAltResult>> {
|
|
|
|
|
let state_clone = Arc::clone(&state);
|
|
|
|
|
let session_clone = Arc::clone(&session);
|
|
|
|
|
|
2025-12-26 08:59:25 -03:00
|
|
|
engine.register_custom_syntax(["USE", "KB", "$expr$"], true, move |context, inputs| {
|
2025-11-21 09:28:35 -03:00
|
|
|
let kb_name = context.eval_expression_tree(&inputs[0])?.to_string();
|
|
|
|
|
|
|
|
|
|
info!(
|
Looking at this diff, I can see it's a comprehensive documentation
update and code refactoring focused on:
1. Adding new documentation pages to the table of contents
2. Restructuring the bot templates documentation
3. Changing keyword syntax from underscore format to space format (e.g.,
`SET_BOT_MEMORY` → `SET BOT MEMORY`)
4. Updating compiler and keyword registration to support the new
space-based syntax
5. Adding new keyword modules (social media, lead scoring, templates,
etc.)
Refactor BASIC keywords to use spaces instead of underscores
Change keyword syntax from underscore format (SET_BOT_MEMORY) to more
natural space-separated format (SET BOT MEMORY) throughout the codebase.
Key changes:
- Update Rhai custom syntax registration to use space tokens
- Simplify compiler preprocessing (fewer replacements needed)
- Update all template .bas files to use new syntax
- Expand documentation with consolidated examples and new sections
- Add new keyword modules: social_media, lead_scoring, send_template,
core_functions, qrcode, sms, procedures, import_export, llm_macros,
on_form_submit
2025-11-30 10:53:59 -03:00
|
|
|
"USE KB keyword executed - KB: {}, Session: {}",
|
2025-11-21 09:28:35 -03:00
|
|
|
kb_name, session_clone.id
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let session_id = session_clone.id;
|
|
|
|
|
let bot_id = session_clone.bot_id;
|
|
|
|
|
let conn = state_clone.conn.clone();
|
2025-11-21 23:23:53 -03:00
|
|
|
let kb_name_clone = kb_name.clone();
|
2025-11-21 09:28:35 -03:00
|
|
|
|
|
|
|
|
let result =
|
2025-11-21 23:23:53 -03:00
|
|
|
std::thread::spawn(move || add_kb_to_session(conn, session_id, bot_id, &kb_name_clone))
|
2025-11-21 09:28:35 -03:00
|
|
|
.join();
|
|
|
|
|
|
|
|
|
|
match result {
|
|
|
|
|
Ok(Ok(_)) => {
|
2025-12-09 07:55:11 -03:00
|
|
|
info!(" KB '{}' added to session {}", kb_name, session_clone.id);
|
2025-11-21 09:28:35 -03:00
|
|
|
Ok(Dynamic::UNIT)
|
|
|
|
|
}
|
|
|
|
|
Ok(Err(e)) => {
|
|
|
|
|
error!("Failed to add KB '{}': {}", kb_name, e);
|
2025-11-21 23:23:53 -03:00
|
|
|
Err(format!("USE_KB failed: {}", e).into())
|
2025-11-21 09:28:35 -03:00
|
|
|
}
|
|
|
|
|
Err(e) => {
|
2025-11-21 23:23:53 -03:00
|
|
|
error!("Thread panic in USE_KB: {:?}", e);
|
|
|
|
|
Err("USE_KB failed: thread panic".into())
|
2025-11-21 09:28:35 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_kb_to_session(
|
|
|
|
|
conn_pool: crate::shared::utils::DbPool,
|
|
|
|
|
session_id: Uuid,
|
|
|
|
|
bot_id: Uuid,
|
|
|
|
|
kb_name: &str,
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
let mut conn = conn_pool
|
|
|
|
|
.get()
|
|
|
|
|
.map_err(|e| format!("Failed to get DB connection: {}", e))?;
|
|
|
|
|
|
2025-11-21 23:23:53 -03:00
|
|
|
let bot_result: BotNameResult = diesel::sql_query("SELECT name FROM bots WHERE id = $1")
|
2025-11-21 09:28:35 -03:00
|
|
|
.bind::<diesel::sql_types::Uuid, _>(bot_id)
|
2025-11-21 23:23:53 -03:00
|
|
|
.get_result(&mut conn)
|
|
|
|
|
.map_err(|e| format!("Failed to get bot name: {}", e))?;
|
|
|
|
|
let bot_name = bot_result.name;
|
2025-11-21 09:28:35 -03:00
|
|
|
|
2025-11-21 23:23:53 -03:00
|
|
|
let kb_exists: Option<KbCollectionResult> = diesel::sql_query(
|
2025-11-21 09:28:35 -03:00
|
|
|
"SELECT folder_path, qdrant_collection FROM kb_collections WHERE bot_id = $1 AND name = $2",
|
|
|
|
|
)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(bot_id)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(kb_name)
|
2025-11-21 23:23:53 -03:00
|
|
|
.get_result(&mut conn)
|
2025-11-21 09:28:35 -03:00
|
|
|
.optional()
|
|
|
|
|
.map_err(|e| format!("Failed to check KB existence: {}", e))?;
|
|
|
|
|
|
2025-11-21 23:23:53 -03:00
|
|
|
let (kb_folder_path, qdrant_collection) = if let Some(kb_result) = kb_exists {
|
|
|
|
|
(kb_result.folder_path, kb_result.qdrant_collection)
|
2025-11-21 09:28:35 -03:00
|
|
|
} else {
|
|
|
|
|
let default_path = format!("work/{}/{}.gbkb/{}", bot_name, bot_name, kb_name);
|
|
|
|
|
let default_collection = format!("{}_{}", bot_name, kb_name);
|
|
|
|
|
|
|
|
|
|
warn!(
|
|
|
|
|
"KB '{}' not found in kb_collections for bot {}. Using default path: {}",
|
|
|
|
|
kb_name, bot_name, default_path
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let kb_id = Uuid::new_v4();
|
|
|
|
|
diesel::sql_query(
|
|
|
|
|
"INSERT INTO kb_collections (id, bot_id, name, folder_path, qdrant_collection, document_count)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5, 0)
|
|
|
|
|
ON CONFLICT (bot_id, name) DO NOTHING"
|
|
|
|
|
)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(kb_id)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(bot_id)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(kb_name)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(&default_path)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(&default_collection)
|
|
|
|
|
.execute(&mut conn)
|
2025-12-23 18:40:58 -03:00
|
|
|
.ok();
|
2025-11-21 09:28:35 -03:00
|
|
|
|
|
|
|
|
(default_path, default_collection)
|
|
|
|
|
};
|
|
|
|
|
|
Add .env.example with comprehensive configuration template
The commit adds a complete example environment configuration file
documenting all available settings for BotServer, including logging,
database, server, drive, LLM, Redis, email, and feature flags.
Also removes hardcoded environment variable usage throughout the
codebase, replacing them with configuration via config.csv or
appropriate defaults. This includes:
- WhatsApp, Teams, Instagram adapter configurations
- Weather API key handling
- Email and directory service configurations
- Console feature conditionally compiles monitoring code
- Improved logging configuration with library suppression
2025-11-28 13:19:03 -03:00
|
|
|
let tool_name: Option<String> = None;
|
2025-11-21 09:28:35 -03:00
|
|
|
|
|
|
|
|
let assoc_id = Uuid::new_v4();
|
|
|
|
|
diesel::sql_query(
|
|
|
|
|
"INSERT INTO session_kb_associations (id, session_id, bot_id, kb_name, kb_folder_path, qdrant_collection, added_by_tool, is_active)
|
|
|
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, true)
|
|
|
|
|
ON CONFLICT (session_id, kb_name)
|
|
|
|
|
DO UPDATE SET
|
|
|
|
|
is_active = true,
|
|
|
|
|
added_at = NOW(),
|
|
|
|
|
added_by_tool = EXCLUDED.added_by_tool"
|
|
|
|
|
)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(assoc_id)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(session_id)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(bot_id)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(kb_name)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(&kb_folder_path)
|
|
|
|
|
.bind::<diesel::sql_types::Text, _>(&qdrant_collection)
|
|
|
|
|
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(tool_name.as_deref())
|
|
|
|
|
.execute(&mut conn)
|
|
|
|
|
.map_err(|e| format!("Failed to add KB association: {}", e))?;
|
|
|
|
|
|
|
|
|
|
info!(
|
2025-12-09 07:55:11 -03:00
|
|
|
" Added KB '{}' to session {} (collection: {}, path: {})",
|
2025-11-21 09:28:35 -03:00
|
|
|
kb_name, session_id, qdrant_collection, kb_folder_path
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_active_kbs_for_session(
|
|
|
|
|
conn_pool: &crate::shared::utils::DbPool,
|
|
|
|
|
session_id: Uuid,
|
|
|
|
|
) -> Result<Vec<(String, String, String)>, String> {
|
|
|
|
|
let mut conn = conn_pool
|
|
|
|
|
.get()
|
|
|
|
|
.map_err(|e| format!("Failed to get DB connection: {}", e))?;
|
|
|
|
|
|
2025-11-21 23:23:53 -03:00
|
|
|
let results: Vec<ActiveKbResult> = diesel::sql_query(
|
2025-11-21 09:28:35 -03:00
|
|
|
"SELECT kb_name, kb_folder_path, qdrant_collection
|
|
|
|
|
FROM session_kb_associations
|
|
|
|
|
WHERE session_id = $1 AND is_active = true
|
|
|
|
|
ORDER BY added_at DESC",
|
|
|
|
|
)
|
|
|
|
|
.bind::<diesel::sql_types::Uuid, _>(session_id)
|
|
|
|
|
.load(&mut conn)
|
|
|
|
|
.map_err(|e| format!("Failed to get active KBs: {}", e))?;
|
|
|
|
|
|
2025-11-21 23:23:53 -03:00
|
|
|
Ok(results
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|r| (r.kb_name, r.kb_folder_path, r.qdrant_collection))
|
|
|
|
|
.collect())
|
2025-11-21 09:28:35 -03:00
|
|
|
}
|