2026-01-05 01:22:45 +05:00
|
|
|
mod intentclassifier;
|
2026-02-08 07:16:03 +05:00
|
|
|
mod embeddingclassifier;
|
2026-01-05 01:22:45 +05:00
|
|
|
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
2026-02-18 21:08:48 +05:00
|
|
|
use crate::{commands::{self, JCommandsList, JCommand}, config, models};
|
2026-01-05 01:22:45 +05:00
|
|
|
use once_cell::sync::OnceCell;
|
|
|
|
|
|
2026-02-08 07:30:46 +05:00
|
|
|
use crate::DB;
|
|
|
|
|
|
2026-02-18 21:08:48 +05:00
|
|
|
static BACKEND: OnceCell<String> = OnceCell::new();
|
2026-01-05 01:22:45 +05:00
|
|
|
|
|
|
|
|
pub async fn init(commands: &Vec<JCommandsList>) -> Result<(), String> {
|
2026-02-18 21:08:48 +05:00
|
|
|
if BACKEND.get().is_some() {
|
2026-01-05 01:22:45 +05:00
|
|
|
return Ok(());
|
2026-02-18 21:08:48 +05:00
|
|
|
}
|
2026-01-05 01:22:45 +05:00
|
|
|
|
2026-02-18 21:08:48 +05:00
|
|
|
let backend = DB.get().unwrap().read().intent_backend.clone();
|
2026-02-08 07:30:46 +05:00
|
|
|
|
2026-02-18 21:08:48 +05:00
|
|
|
BACKEND.set(backend.clone()).map_err(|_| "Backend already set")?;
|
2026-01-05 01:22:45 +05:00
|
|
|
|
2026-02-18 21:08:48 +05:00
|
|
|
match backend.as_str() {
|
|
|
|
|
"none" => {
|
|
|
|
|
info!("Intent recognition disabled");
|
|
|
|
|
}
|
|
|
|
|
"intent-classifier" => {
|
|
|
|
|
info!("Initializing IntentClassifier backend.");
|
2026-01-05 01:22:45 +05:00
|
|
|
intentclassifier::init(&commands).await?;
|
2026-02-18 21:08:48 +05:00
|
|
|
info!("IntentClassifier backend initialized.");
|
|
|
|
|
}
|
|
|
|
|
// any other value is treated as a model ID for embedding classification
|
|
|
|
|
model_id => {
|
|
|
|
|
info!("Initializing EmbeddingClassifier with model '{}'.", model_id);
|
|
|
|
|
let model = models::embedding::load(models::registry(), model_id)?;
|
|
|
|
|
embeddingclassifier::init_with_model(model, &commands)?;
|
|
|
|
|
info!("EmbeddingClassifier backend initialized.");
|
|
|
|
|
}
|
2026-01-05 01:22:45 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn classify(text: &str) -> Option<(String, f64)> {
|
2026-02-18 21:08:48 +05:00
|
|
|
match BACKEND.get()?.as_str() {
|
|
|
|
|
"none" => None,
|
|
|
|
|
"intent-classifier" => {
|
2026-01-05 01:22:45 +05:00
|
|
|
match intentclassifier::classify(text).await {
|
|
|
|
|
Ok(prediction) => {
|
|
|
|
|
let confidence = prediction.confidence.value();
|
|
|
|
|
if confidence >= config::INTENT_CLASSIFIER_MIN_CONFIDENCE {
|
|
|
|
|
Some((prediction.intent.to_string(), confidence))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Intent classification error: {}", e);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-18 21:08:48 +05:00
|
|
|
_ => {
|
2026-02-08 07:16:03 +05:00
|
|
|
match embeddingclassifier::classify(text) {
|
|
|
|
|
Ok((intent_id, confidence)) => {
|
|
|
|
|
if confidence >= config::EMBEDDING_MIN_CONFIDENCE {
|
|
|
|
|
Some((intent_id, confidence))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Embedding classification error: {}", e);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-05 01:22:45 +05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-18 21:08:48 +05:00
|
|
|
// unified command lookup by intent ID - works for all backends
|
|
|
|
|
pub fn get_command_by_intent<'a>(
|
|
|
|
|
commands: &'a [JCommandsList],
|
|
|
|
|
intent_id: &str,
|
|
|
|
|
) -> Option<(&'a PathBuf, &'a JCommand)> {
|
|
|
|
|
if matches!(BACKEND.get().map(|s| s.as_str()), Some("none")) {
|
|
|
|
|
return None;
|
2026-01-05 01:22:45 +05:00
|
|
|
}
|
2026-02-18 21:08:48 +05:00
|
|
|
commands::get_command_by_id(commands, intent_id)
|
|
|
|
|
}
|