use anyhow::{Result, bail};
use crate::rlm::{RlmChunker, RlmResult};
pub fn bounded_summary(result: RlmResult, target_tokens: usize) -> Result<String> {
if !result.success {
bail!(
"RLM summary did not converge: {}",
result.error.unwrap_or_else(|| "unknown reason".to_string())
);
}
let clean = strip_stats_header(&result.processed);
let trimmed = clean.trim();
if trimmed.is_empty() {
bail!("RLM summary produced empty body");
}
Ok(clamp_tokens(trimmed, target_tokens))
}
pub fn strip_stats_header(text: &str) -> &str {
let Some(rest) = text.strip_prefix("[RLM: ") else {
return text;
};
rest.split_once("\n\n").map_or(text, |(_, body)| body)
}
pub fn clamp_tokens(text: &str, target_tokens: usize) -> String {
let limit = target_tokens.saturating_mul(4).max(64);
if RlmChunker::estimate_tokens(text) <= target_tokens || text.len() <= limit {
return text.to_string();
}
let marker = "\n[summary clamped to requested token budget]";
let keep = limit.saturating_sub(marker.len());
let end = (0..=keep)
.rev()
.find(|index| text.is_char_boundary(*index))
.unwrap_or(0);
format!("{}{}", text[..end].trim_end(), marker)
}