use super::estimator::TokenEstimator;
pub(crate) const JOINER: &str = "\n\n";
#[derive(Debug)]
pub(crate) struct PackItem {
pub seq: usize,
pub critical: bool,
pub priority: u8,
pub relevance: f32,
pub pieces: Vec<String>,
}
pub(crate) fn pack(items: &[PackItem], usable: u64, estimator: &dyn TokenEstimator) -> Vec<usize> {
let mut taken = vec![0_usize; items.len()];
let mut remaining = usable;
let joiner_tokens = estimator.estimate(JOINER);
for &index in &selection_order(items) {
let item = &items[index];
taken[index] = take_pieces(&item.pieces, &mut remaining, joiner_tokens, estimator);
}
taken
}
fn selection_order(items: &[PackItem]) -> Vec<usize> {
let mut order: Vec<usize> = (0..items.len()).collect();
order.sort_by(|&a, &b| {
let (left, right) = (&items[a], &items[b]);
right
.critical
.cmp(&left.critical)
.then_with(|| right.priority.cmp(&left.priority))
.then_with(|| right.relevance.total_cmp(&left.relevance))
.then_with(|| left.seq.cmp(&right.seq))
});
order
}
fn take_pieces(
pieces: &[String],
remaining: &mut u64,
joiner_tokens: u64,
estimator: &dyn TokenEstimator,
) -> usize {
let mut count = 0_usize;
for piece in pieces {
let joiner = if count == 0 { joiner_tokens } else { 0 };
let cost = estimator.estimate(piece).saturating_add(joiner);
if cost > *remaining {
break;
}
*remaining -= cost;
count += 1;
}
count
}
#[cfg(test)]
#[path = "budget_tests.rs"]
mod tests;