use serde_json::Value;
use super::{observability, smart_harness, trim};
pub(super) async fn compress(
request: super::compress::CompressRequest<'_>,
summarizer: Option<&super::compress::SummarizeFn>,
state: &mut super::CompressState,
harness: Option<&smart_harness::SmartHarness>,
) -> anyhow::Result<super::compress::CompressOutcome> {
let outcome = smart_harness::compress(request, summarizer, state, harness).await?;
if outcome.tokens_after <= request.budget || !request.rewrites_history || harness.is_some() {
return Ok(outcome);
}
let head =
trim::protected_prompt_head_len(request.messages, super::prompt_read::ACTIVE_PROMPT_PREFIX);
let tail = protected_tool_tail(request.messages)
.max(request.replay_protected_tail_len)
.max(1);
let mut bounded = request;
bounded.max_messages = Some(head.saturating_add(1).saturating_add(tail));
smart_harness::compress(bounded, summarizer, state, harness).await
}
pub(super) fn protected_tool_tail(messages: &[Value]) -> usize {
let Some(result) = messages.iter().rposition(|m| m["role"] == "tool") else {
return 0;
};
let call = messages[..result]
.iter()
.rposition(|m| m["role"] == "assistant" && m["tool_calls"].is_array())
.unwrap_or(result);
messages.len() - call
}
pub(super) fn target(
messages: &[Value],
budget: usize,
est: crate::tokens::TokenEstimation,
) -> usize {
budget.min(trim::estimate_tokens(messages, est).saturating_sub(1))
}
pub(super) fn record(
observation: &mut Option<&mut observability::SolveObservation>,
harness: Option<&smart_harness::SmartHarness>,
round: usize,
attempt: u32,
estimated_tokens: usize,
projected_tokens: Option<usize>,
) -> anyhow::Result<()> {
let event = observability::BehaviorSignal::ContextExceeded {
round,
attempt,
estimated_tokens,
projected_tokens,
};
if let Some(observation) = observation.as_deref_mut() {
observation.behavior_signals.push(event.clone());
}
if let Some(harness) = harness {
harness.context_exceeded(&event)?;
}
Ok(())
}
#[derive(Debug, Clone, Copy)]
pub(super) struct PromptMeasurement {
pub tokens: usize,
pub estimated_tokens: usize,
}
impl PromptMeasurement {
pub(super) fn learn(&self, state: &mut super::CompressState) {
state
.calibration
.observe_count(self.tokens, self.estimated_tokens);
}
}
impl std::fmt::Display for PromptMeasurement {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
formatter,
"measured {} prompt tokens from a {}-token prior",
self.tokens, self.estimated_tokens
)
}
}
#[derive(Debug)]
pub(super) struct OptionalRejection {
pub estimated_tokens: usize,
}
impl std::fmt::Display for OptionalRejection {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
formatter,
"optional completion rejected (~{} input tokens)",
self.estimated_tokens
)
}
}
pub(super) fn terminal_optional(
observations: &mut Option<&mut observability::SolveObservation>,
state: &mut super::CompressState,
applied_ratio: f32,
round: usize,
attempt: u32,
estimated_tokens: usize,
) -> anyhow::Result<()> {
state.calibration.overflow(applied_ratio);
record(observations, None, round, attempt, estimated_tokens, None)
}
#[cfg(test)]
#[path = "context_recovery_tests.rs"]
mod tests;