feat: make tool result pruning opt-in
This commit is contained in:
@@ -577,6 +577,8 @@ pub struct PermissionConfigPartial {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct CompactionConfigPartial {
|
||||
#[serde(default)]
|
||||
pub prune_enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub prune_protected_tokens: Option<u64>,
|
||||
#[serde(default)]
|
||||
@@ -912,6 +914,7 @@ impl PermissionConfigPartial {
|
||||
impl CompactionConfigPartial {
|
||||
fn merge(self, upper: Self) -> Self {
|
||||
Self {
|
||||
prune_enabled: upper.prune_enabled.or(self.prune_enabled),
|
||||
prune_protected_tokens: upper.prune_protected_tokens.or(self.prune_protected_tokens),
|
||||
prune_min_savings: upper.prune_min_savings.or(self.prune_min_savings),
|
||||
threshold: upper.threshold.or(self.threshold),
|
||||
@@ -1223,6 +1226,7 @@ impl TryFrom<WorkerManifestConfig> for WorkerManifest {
|
||||
validate_model_paths(cm, "compaction.model.auth.file")?;
|
||||
}
|
||||
Ok(CompactionConfig {
|
||||
prune_enabled: c.prune_enabled.unwrap_or(defaults::PRUNE_ENABLED),
|
||||
prune_protected_tokens: c
|
||||
.prune_protected_tokens
|
||||
.unwrap_or(defaults::PRUNE_PROTECTED_TOKENS),
|
||||
@@ -1831,6 +1835,7 @@ mod tests {
|
||||
let lower = WorkerManifestConfig {
|
||||
compaction: Some(CompactionConfigPartial {
|
||||
threshold: Some(50_000),
|
||||
prune_enabled: Some(false),
|
||||
prune_protected_tokens: Some(5_000),
|
||||
..Default::default()
|
||||
}),
|
||||
@@ -1861,7 +1866,8 @@ mod tests {
|
||||
let merged = lower.merge(upper);
|
||||
let c = merged.compaction.unwrap();
|
||||
assert_eq!(c.threshold, Some(80_000));
|
||||
// field from lower retained when upper has None
|
||||
// fields from lower retained when upper has None
|
||||
assert_eq!(c.prune_enabled, Some(false));
|
||||
assert_eq!(c.prune_protected_tokens, Some(5_000));
|
||||
let search = merged.web.unwrap().search.unwrap();
|
||||
assert_eq!(search.timeout_secs, Some(3));
|
||||
|
||||
@@ -14,6 +14,10 @@ pub const TOOL_OUTPUT_MAX_BYTES: usize = 64 * 1024;
|
||||
/// See [`crate::FileUploadLimits`].
|
||||
pub const FILE_UPLOAD_MAX_BYTES: usize = 256 * 1024;
|
||||
|
||||
/// Whether request-time tool-result pruning is enabled when `[compaction]`
|
||||
/// exists. See [`crate::CompactionConfig::prune_enabled`].
|
||||
pub const PRUNE_ENABLED: bool = false;
|
||||
|
||||
/// Token budget at the history tail protected from pruning. See
|
||||
/// [`crate::CompactionConfig::prune_protected_tokens`].
|
||||
pub const PRUNE_PROTECTED_TOKENS: u64 = 8000;
|
||||
|
||||
@@ -768,6 +768,11 @@ pub enum ToolPermissionAction {
|
||||
/// (full history summarisation). Omitting `[compaction]` disables both.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CompactionConfig {
|
||||
/// Whether request-time projection removes detail from old tool results.
|
||||
/// Independent from the compaction thresholds below.
|
||||
#[serde(default = "default_prune_enabled")]
|
||||
pub prune_enabled: bool,
|
||||
|
||||
/// Token budget at the history tail protected from pruning.
|
||||
#[serde(default = "default_prune_protected_tokens")]
|
||||
pub prune_protected_tokens: u64,
|
||||
@@ -868,6 +873,9 @@ pub struct CompactionConfig {
|
||||
pub model: Option<ModelManifest>,
|
||||
}
|
||||
|
||||
fn default_prune_enabled() -> bool {
|
||||
defaults::PRUNE_ENABLED
|
||||
}
|
||||
fn default_prune_protected_tokens() -> u64 {
|
||||
defaults::PRUNE_PROTECTED_TOKENS
|
||||
}
|
||||
@@ -914,6 +922,7 @@ fn default_result_context_max_tokens() -> u64 {
|
||||
impl Default for CompactionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
prune_enabled: default_prune_enabled(),
|
||||
prune_protected_tokens: default_prune_protected_tokens(),
|
||||
prune_min_savings: default_prune_min_savings(),
|
||||
threshold: None,
|
||||
@@ -1460,9 +1469,11 @@ model_id = "claude-sonnet-4-20250514"
|
||||
|
||||
#[test]
|
||||
fn parse_compaction_config() {
|
||||
let toml = format!("{MINIMAL_REQUIRED}\n[compaction]\nthreshold = 80000\n");
|
||||
let toml =
|
||||
format!("{MINIMAL_REQUIRED}\n[compaction]\nprune_enabled = false\nthreshold = 80000\n");
|
||||
let manifest = WorkerManifest::from_toml(&toml).unwrap();
|
||||
let c = manifest.compaction.unwrap();
|
||||
assert!(!c.prune_enabled);
|
||||
assert_eq!(c.prune_protected_tokens, 8000);
|
||||
assert_eq!(c.prune_min_savings, 4096);
|
||||
assert_eq!(c.threshold, Some(80000));
|
||||
@@ -1471,6 +1482,11 @@ model_id = "claude-sonnet-4-20250514"
|
||||
assert_eq!(c.worker_max_turns, Some(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compaction_defaults_to_pruning_disabled() {
|
||||
assert!(!CompactionConfig::default().prune_enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reject_removed_prune_protected_turns_field() {
|
||||
let toml = format!("{MINIMAL_REQUIRED}\n[compaction]\nprune_protected_turns = 3\n");
|
||||
|
||||
@@ -1245,6 +1245,26 @@ mod tests {
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
for name in [
|
||||
"default",
|
||||
"intake",
|
||||
"orchestrator",
|
||||
"companion",
|
||||
"coder",
|
||||
"reviewer",
|
||||
"memory-consolidation",
|
||||
] {
|
||||
let role = resolve(name);
|
||||
assert_eq!(
|
||||
role.manifest
|
||||
.compaction
|
||||
.as_ref()
|
||||
.map(|compaction| compaction.prune_enabled),
|
||||
Some(false),
|
||||
"{name} must keep pruning disabled for the experiment"
|
||||
);
|
||||
}
|
||||
|
||||
let companion = resolve("companion");
|
||||
assert_eq!(
|
||||
companion.manifest.model.ref_.as_deref(),
|
||||
|
||||
Reference in New Issue
Block a user