briefcase-node 2.4.1

Node.js bindings for Briefcase AI
Documentation
//! Node.js bindings for drift detection

use napi::{Result, bindgen_prelude::*};
use std::collections::HashMap;
use briefcase_core::{DriftCalculator, DriftMetrics, DriftStatus};

/// Node.js wrapper for DriftCalculator
#[napi]
pub struct JsDriftCalculator {
    inner: DriftCalculator,
}

#[napi]
impl JsDriftCalculator {
    #[napi(constructor)]
    pub fn new() -> Self {
        Self {
            inner: DriftCalculator::new(),
        }
    }

    #[napi]
    pub fn calculate_drift(&self, outputs: Vec<String>) -> JsDriftMetrics {
        let metrics = self.inner.calculate_drift(&outputs);
        JsDriftMetrics { inner: metrics }
    }

    #[napi]
    pub fn with_similarity_threshold(&mut self, threshold: f64) -> Result<()> {
        if threshold < 0.0 || threshold > 1.0 {
            return Err(napi::Error::from_reason(
                "Similarity threshold must be between 0.0 and 1.0"
            ));
        }
        self.inner = self.inner.clone().with_similarity_threshold(threshold);
        Ok(())
    }

    #[napi]
    pub fn with_consensus_threshold(&mut self, threshold: f64) -> Result<()> {
        if threshold < 0.0 || threshold > 1.0 {
            return Err(napi::Error::from_reason(
                "Consensus threshold must be between 0.0 and 1.0"
            ));
        }
        self.inner = self.inner.clone().with_consensus_threshold(threshold);
        Ok(())
    }

    #[napi(getter)]
    pub fn similarity_threshold(&self) -> f64 {
        self.inner.similarity_threshold
    }

    #[napi(getter)]
    pub fn consensus_threshold(&self) -> f64 {
        self.inner.consensus_threshold
    }
}

/// Node.js wrapper for DriftMetrics
#[napi]
pub struct JsDriftMetrics {
    inner: DriftMetrics,
}

#[napi]
impl JsDriftMetrics {
    #[napi(getter)]
    pub fn consistency_score(&self) -> f64 {
        self.inner.consistency_score
    }

    #[napi(getter)]
    pub fn agreement_rate(&self) -> f64 {
        self.inner.agreement_rate
    }

    #[napi(getter)]
    pub fn drift_score(&self) -> f64 {
        self.inner.drift_score
    }

    #[napi(getter)]
    pub fn consensus_output(&self) -> Option<String> {
        self.inner.consensus_output.clone()
    }

    #[napi(getter)]
    pub fn outliers(&self) -> Vec<u32> {
        self.inner.outliers.iter().map(|&i| i as u32).collect()
    }

    #[napi(getter)]
    pub fn status(&self) -> String {
        match self.inner.status {
            DriftStatus::Stable => "stable".to_string(),
            DriftStatus::Minor => "minor".to_string(),
            DriftStatus::Significant => "significant".to_string(),
            DriftStatus::Severe => "severe".to_string(),
        }
    }

    #[napi(getter)]
    pub fn total_samples(&self) -> u32 {
        self.inner.total_samples as u32
    }

    #[napi(getter)]
    pub fn unique_outputs(&self) -> u32 {
        self.inner.unique_outputs as u32
    }

    #[napi]
    pub fn to_object(&self) -> Result<serde_json::Value> {
        Ok(serde_json::json!({
            "consistency_score": self.inner.consistency_score,
            "agreement_rate": self.inner.agreement_rate,
            "drift_score": self.inner.drift_score,
            "consensus_output": self.inner.consensus_output,
            "outliers": self.inner.outliers,
            "status": self.status(),
            "total_samples": self.inner.total_samples,
            "unique_outputs": self.inner.unique_outputs,
        }))
    }
}