use awaken_runtime_contract::contract::inference::ContextWindowPolicy;
use awaken_runtime_contract::registry_spec::ModelSpec;
pub fn effective_policy(policy: &ContextWindowPolicy, model: &ModelSpec) -> ContextWindowPolicy {
let mut out = policy.clone();
if let Some(cap) = model.context_window {
out.max_context_tokens = out.max_context_tokens.min(cap as usize);
}
if let Some(cap) = model.max_output_tokens {
out.max_output_tokens = out.max_output_tokens.min(cap as usize);
}
out.max_output_tokens = out.max_output_tokens.min(out.max_context_tokens);
if let Some(threshold) = out.autocompact_threshold {
let usable_input = out.max_context_tokens.saturating_sub(out.max_output_tokens);
out.autocompact_threshold = if usable_input == 0 {
None
} else {
Some(threshold.min(usable_input))
};
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn effective_policy_clamps_max_context_to_model_capability() {
let policy = ContextWindowPolicy {
max_context_tokens: 200_000,
max_output_tokens: 16_384,
..Default::default()
};
let model = ModelSpec {
context_window: Some(32_000),
max_output_tokens: Some(4_096),
..ModelSpec::new("m", "p", "u")
};
let eff = effective_policy(&policy, &model);
assert_eq!(eff.max_context_tokens, 32_000);
assert_eq!(eff.max_output_tokens, 4_096);
}
#[test]
fn effective_policy_passes_through_when_model_has_no_caps() {
let policy = ContextWindowPolicy {
max_context_tokens: 200_000,
max_output_tokens: 16_384,
..Default::default()
};
let model = ModelSpec::new("m", "p", "u");
let eff = effective_policy(&policy, &model);
assert_eq!(eff.max_context_tokens, 200_000);
assert_eq!(eff.max_output_tokens, 16_384);
}
#[test]
fn effective_policy_only_narrows_never_widens() {
let policy = ContextWindowPolicy {
max_context_tokens: 8_000,
max_output_tokens: 2_000,
..Default::default()
};
let model = ModelSpec {
context_window: Some(1_000_000),
max_output_tokens: Some(100_000),
..ModelSpec::new("m", "p", "u")
};
let eff = effective_policy(&policy, &model);
assert_eq!(eff.max_context_tokens, 8_000);
assert_eq!(eff.max_output_tokens, 2_000);
}
#[test]
fn effective_policy_preserves_other_fields() {
let policy = ContextWindowPolicy {
max_context_tokens: 200_000,
max_output_tokens: 16_384,
min_recent_messages: 7,
enable_prompt_cache: false,
autocompact_threshold: Some(50_000),
compaction_raw_suffix_messages: 5,
..Default::default()
};
let model = ModelSpec {
context_window: Some(100_000),
..ModelSpec::new("m", "p", "u")
};
let eff = effective_policy(&policy, &model);
assert_eq!(eff.max_context_tokens, 100_000);
assert_eq!(eff.min_recent_messages, 7);
assert!(!eff.enable_prompt_cache);
assert_eq!(eff.autocompact_threshold, Some(50_000));
assert_eq!(eff.compaction_raw_suffix_messages, 5);
}
#[test]
fn effective_policy_caps_output_at_clamped_context_when_model_lacks_output_cap() {
let policy = ContextWindowPolicy {
max_context_tokens: 200_000,
max_output_tokens: 16_384,
..Default::default()
};
let model = ModelSpec {
context_window: Some(8_000),
max_output_tokens: None,
..ModelSpec::new("m", "p", "u")
};
let eff = effective_policy(&policy, &model);
assert_eq!(eff.max_context_tokens, 8_000);
assert!(
eff.max_output_tokens <= eff.max_context_tokens,
"output {} must not exceed context {}",
eff.max_output_tokens,
eff.max_context_tokens
);
assert_eq!(eff.max_output_tokens, 8_000);
}
#[test]
fn effective_policy_caps_output_when_policy_alone_inverts_invariant() {
let policy = ContextWindowPolicy {
max_context_tokens: 4_000,
max_output_tokens: 8_000, ..Default::default()
};
let model = ModelSpec::new("m", "p", "u");
let eff = effective_policy(&policy, &model);
assert_eq!(eff.max_context_tokens, 4_000);
assert_eq!(eff.max_output_tokens, 4_000);
}
#[test]
fn effective_policy_clamps_autocompact_threshold_to_usable_input() {
let policy = ContextWindowPolicy {
max_context_tokens: 200_000,
max_output_tokens: 16_384,
autocompact_threshold: Some(150_000),
..Default::default()
};
let model = ModelSpec {
context_window: Some(100_000),
..ModelSpec::new("m", "p", "u")
};
let eff = effective_policy(&policy, &model);
let usable = eff.max_context_tokens - eff.max_output_tokens; assert_eq!(eff.autocompact_threshold, Some(usable));
}
#[test]
fn effective_policy_drops_autocompact_threshold_when_no_usable_input() {
let policy = ContextWindowPolicy {
max_context_tokens: 200_000,
max_output_tokens: 16_384,
autocompact_threshold: Some(50_000),
..Default::default()
};
let model = ModelSpec {
context_window: Some(8_000),
max_output_tokens: Some(8_000),
..ModelSpec::new("m", "p", "u")
};
let eff = effective_policy(&policy, &model);
assert_eq!(eff.max_context_tokens, 8_000);
assert_eq!(eff.max_output_tokens, 8_000);
assert_eq!(
eff.autocompact_threshold, None,
"no usable input budget => no point in auto-compaction"
);
}
#[test]
fn effective_policy_leaves_autocompact_threshold_below_usable_input_untouched() {
let policy = ContextWindowPolicy {
max_context_tokens: 200_000,
max_output_tokens: 16_384,
autocompact_threshold: Some(50_000), ..Default::default()
};
let model = ModelSpec {
context_window: Some(200_000),
max_output_tokens: Some(16_384),
..ModelSpec::new("m", "p", "u")
};
let eff = effective_policy(&policy, &model);
assert_eq!(eff.autocompact_threshold, Some(50_000));
}
}