Finish long-running agent parity
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::engine::ChatTurn;
|
||||
|
||||
pub(crate) const SUMMARY_MAX_TOKENS: i32 = 4096;
|
||||
pub(crate) const TOOL_RESULT_RESERVE_TOKENS: u32 = 1024;
|
||||
const MIN_SUMMARY_TOKENS: u32 = 256;
|
||||
|
||||
const SOFT_PERCENT: u32 = 85;
|
||||
const MIN_FREE_TOKENS: u32 = 8192;
|
||||
@@ -21,24 +22,23 @@ pub(crate) fn should_compact(used: u32, limit: u32) -> bool {
|
||||
|| limit.saturating_sub(used) <= MIN_FREE_TOKENS.min(limit / 8)
|
||||
}
|
||||
|
||||
pub(crate) fn tool_result_needs_compaction(used: u32, limit: u32, result: &str) -> bool {
|
||||
should_compact(used, limit)
|
||||
|| used
|
||||
.saturating_add(result.len().min(u32::MAX as usize) as u32)
|
||||
.saturating_add(TOOL_RESULT_RESERVE_TOKENS)
|
||||
>= limit
|
||||
pub(crate) fn tool_result_reserve(limit: u32) -> u32 {
|
||||
TOOL_RESULT_RESERVE_TOKENS.min((limit / 8).max(16))
|
||||
}
|
||||
|
||||
pub(crate) fn bounded_tool_result(used: u32, limit: u32, result: String) -> String {
|
||||
if used
|
||||
.saturating_add(result.len().min(u32::MAX as usize) as u32)
|
||||
.saturating_add(TOOL_RESULT_RESERVE_TOKENS)
|
||||
< limit
|
||||
{
|
||||
result
|
||||
} else {
|
||||
"Tool error: the result is too large for the remaining context after compaction. Retry with a smaller read/search/bash output.\n".into()
|
||||
}
|
||||
pub(crate) fn tool_result_fits(projected: u32, limit: u32, reserve: u32) -> bool {
|
||||
limit > 0 && projected.saturating_add(reserve) < limit
|
||||
}
|
||||
|
||||
pub(crate) fn bounded_tool_error(projected: u32, limit: u32, reserve: u32) -> String {
|
||||
format!(
|
||||
"Tool error: tool result still does not fit after context compaction (projected_prompt={projected} tokens, ctx={limit}, reserve={reserve}). Retry with a smaller read/search/bash output.\n"
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn summary_budget(prompt: u32, limit: u32) -> Option<i32> {
|
||||
let room = limit.saturating_sub(prompt).saturating_sub(1);
|
||||
(room >= MIN_SUMMARY_TOKENS).then_some(room.min(SUMMARY_MAX_TOKENS as u32) as i32)
|
||||
}
|
||||
|
||||
pub(crate) fn tail_budget(context: u32) -> u32 {
|
||||
@@ -132,11 +132,30 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oversized_tool_result_becomes_a_bounded_retry_error() {
|
||||
let result = "x".repeat(4_000);
|
||||
assert!(tool_result_needs_compaction(6_000, 10_000, &result));
|
||||
let error = bounded_tool_result(4_000, 5_000, result);
|
||||
fn a_new_summary_replaces_the_previous_rebuild_summary() {
|
||||
let current = summary_system_prompt("tools", Some("old state"));
|
||||
assert!(current.contains("old state"));
|
||||
let rebuilt = summary_system_prompt("tools", Some("new state"));
|
||||
assert!(rebuilt.contains("new state"));
|
||||
assert!(!rebuilt.contains("old state"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hard_trigger_and_retry_reserve_match_the_reference() {
|
||||
assert_eq!(tool_result_reserve(4096), 512);
|
||||
assert_eq!(tool_result_reserve(65_536), 1024);
|
||||
assert!(tool_result_fits(3000, 4096, 512));
|
||||
assert!(!tool_result_fits(3584, 4096, 512));
|
||||
let error = bounded_tool_error(5000, 4096, 512);
|
||||
assert!(error.starts_with("Tool error:"));
|
||||
assert!(error.len() < 160);
|
||||
assert!(error.contains("projected_prompt=5000 tokens"));
|
||||
assert!(error.len() < 256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summary_budget_keeps_answer_room_and_rejects_exhausted_contexts() {
|
||||
assert_eq!(summary_budget(1000, 8192), Some(SUMMARY_MAX_TOKENS));
|
||||
assert_eq!(summary_budget(7900, 8192), Some(291));
|
||||
assert_eq!(summary_budget(7936, 8192), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user