use crate::limits;
use crate::tokenizer;
#[derive(Debug, Clone, Copy)]
pub enum BudgetTarget {
Tokens(usize),
Bytes(usize),
}
impl BudgetTarget {
pub fn clamped(self) -> Self {
match self {
BudgetTarget::Tokens(limit) => {
BudgetTarget::Tokens(limits::clamp_budget_tokens(limit))
}
BudgetTarget::Bytes(limit) => BudgetTarget::Bytes(limits::clamp_budget_bytes(limit)),
}
}
}
#[derive(Debug, Clone)]
pub struct Budget {
pub target: BudgetTarget,
pub used: usize,
}
impl Budget {
pub fn new(target: BudgetTarget) -> Self {
Self {
target: target.clamped(),
used: 0,
}
}
pub fn tokens(limit: usize) -> Self {
Self::new(BudgetTarget::Tokens(limit))
}
pub fn bytes(limit: usize) -> Self {
Self::new(BudgetTarget::Bytes(limit))
}
pub fn limit(&self) -> usize {
match self.target {
BudgetTarget::Tokens(t) => t,
BudgetTarget::Bytes(b) => b,
}
}
pub fn remaining(&self) -> usize {
self.limit().saturating_sub(self.used)
}
pub fn would_fit(&self, content: &str) -> bool {
self.cost(content) <= self.remaining()
}
pub fn try_add(&mut self, content: &str) -> bool {
let cost = self.cost(content);
if cost <= self.remaining() {
self.used += cost;
true
} else {
false
}
}
pub fn add(&mut self, content: &str) {
self.used += self.cost(content);
}
fn cost(&self, content: &str) -> usize {
match self.target {
BudgetTarget::Tokens(_) => tokenizer::count_tokens(content),
BudgetTarget::Bytes(_) => content.len(),
}
}
pub fn usage_fraction(&self) -> f64 {
if self.limit() == 0 {
return 1.0;
}
self.used as f64 / self.limit() as f64
}
pub fn is_exhausted(&self) -> bool {
self.remaining() == 0
}
}