#![forbid(unsafe_code)]
pub mod heuristic;
pub mod manage;
#[cfg(feature = "llama")]
pub mod llama;
use kintsugi_core::{Class, ProposedCommand};
pub use heuristic::HeuristicScorer;
pub use manage::{select_spec, ModelSpec, MODEL_FALLBACK, MODEL_PRIMARY};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModelOutput {
pub summary: String,
pub risk: u8,
}
pub trait Scorer: Send + Sync {
fn name(&self) -> &str;
fn score(&self, cmd: &ProposedCommand, class: Class, rule: &str) -> ModelOutput;
}
pub fn model_available() -> bool {
cfg!(feature = "llama")
}
pub fn default_scorer() -> Box<dyn Scorer> {
#[cfg(feature = "llama")]
{
match llama::LlamaScorer::autoload() {
Ok(s) => return Box::new(s),
Err(e) => eprintln!("kintsugi-model: falling back to heuristic scorer: {e}"),
}
}
Box::new(HeuristicScorer::new())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_scorer_is_usable() {
let s = default_scorer();
let cmd = ProposedCommand::new("t", "/tmp", vec!["rm".into()], "rm -rf build");
let out = s.score(&cmd, Class::Ambiguous, "ambiguous:rm");
assert!(!out.summary.is_empty());
assert!(out.risk <= 100);
}
#[test]
fn model_available_tracks_feature() {
assert_eq!(model_available(), cfg!(feature = "llama"));
}
}