use napi::{Result, bindgen_prelude::*};
use briefcase_core::{CostCalculator, CostEstimate, BudgetStatus, BudgetAlert};
#[napi]
pub struct JsCostCalculator {
inner: CostCalculator,
}
#[napi]
impl JsCostCalculator {
#[napi(constructor)]
pub fn new() -> Self {
Self {
inner: CostCalculator::new(),
}
}
#[napi]
pub fn estimate_cost(&self, model_name: String, input_tokens: u32, output_tokens: u32) -> Result<JsCostEstimate> {
match self.inner.estimate_cost(&model_name, input_tokens, output_tokens) {
Ok(estimate) => Ok(JsCostEstimate { inner: estimate }),
Err(e) => Err(napi::Error::from_reason(e.to_string())),
}
}
#[napi]
pub fn estimate_cost_from_text(&self, model_name: String, input_text: String, output_text: String) -> Result<JsCostEstimate> {
match self.inner.estimate_cost_from_text(&model_name, &input_text, &output_text) {
Ok(estimate) => Ok(JsCostEstimate { inner: estimate }),
Err(e) => Err(napi::Error::from_reason(e.to_string())),
}
}
#[napi]
pub fn project_cost(&self, model_name: String, input_tokens: u32, output_tokens: u32, num_calls: u32) -> Result<f64> {
match self.inner.project_cost(&model_name, input_tokens, output_tokens, num_calls) {
Ok(cost) => Ok(cost),
Err(e) => Err(napi::Error::from_reason(e.to_string())),
}
}
#[napi]
pub fn compare_models(&self, models: Vec<String>, input_tokens: u32, output_tokens: u32) -> Result<Vec<JsCostEstimate>> {
match self.inner.compare_models(&models, input_tokens, output_tokens) {
Ok(comparisons) => {
Ok(comparisons.into_iter().map(|c| JsCostEstimate { inner: c }).collect())
}
Err(e) => Err(napi::Error::from_reason(e.to_string())),
}
}
#[napi]
pub fn get_cheapest_model(&self, models: Vec<String>, input_tokens: u32, output_tokens: u32) -> Result<String> {
match self.inner.get_cheapest_model(&models, input_tokens, output_tokens) {
Ok(model) => Ok(model),
Err(e) => Err(napi::Error::from_reason(e.to_string())),
}
}
#[napi]
pub fn check_budget(&self, current_spend: f64, budget_limit: f64) -> JsBudgetStatus {
let status = self.inner.check_budget(current_spend, budget_limit);
JsBudgetStatus { inner: status }
}
#[napi]
pub fn estimate_tokens(&self, text: String) -> u32 {
self.inner.estimate_tokens(&text)
}
}
#[napi]
pub struct JsCostEstimate {
inner: CostEstimate,
}
#[napi]
impl JsCostEstimate {
#[napi(getter)]
pub fn model_name(&self) -> String {
self.inner.model_name.clone()
}
#[napi(getter)]
pub fn provider(&self) -> Option<String> {
self.inner.provider.clone()
}
#[napi(getter)]
pub fn input_tokens(&self) -> u32 {
self.inner.input_tokens
}
#[napi(getter)]
pub fn output_tokens(&self) -> u32 {
self.inner.output_tokens
}
#[napi(getter)]
pub fn input_cost(&self) -> f64 {
self.inner.input_cost
}
#[napi(getter)]
pub fn output_cost(&self) -> f64 {
self.inner.output_cost
}
#[napi(getter)]
pub fn total_cost(&self) -> f64 {
self.inner.total_cost
}
#[napi(getter)]
pub fn cost_per_token(&self) -> f64 {
self.inner.cost_per_token
}
}
#[napi]
pub struct JsBudgetStatus {
inner: BudgetStatus,
}
#[napi]
impl JsBudgetStatus {
#[napi(getter)]
pub fn status(&self) -> String {
match self.inner.status {
BudgetAlert::Green => "green".to_string(),
BudgetAlert::Yellow => "yellow".to_string(),
BudgetAlert::Red => "red".to_string(),
BudgetAlert::Critical => "critical".to_string(),
}
}
#[napi(getter)]
pub fn current_spend(&self) -> f64 {
self.inner.current_spend
}
#[napi(getter)]
pub fn budget_limit(&self) -> f64 {
self.inner.budget_limit
}
#[napi(getter)]
pub fn remaining_budget(&self) -> f64 {
self.inner.remaining_budget
}
#[napi(getter)]
pub fn percent_used(&self) -> f64 {
self.inner.percent_used
}
#[napi(getter)]
pub fn message(&self) -> String {
self.inner.message.clone()
}
}