Allow models to enable token budgeting by default (#41803)

## What changed

- Add model metadata flags for enabling token budgeting and its history-notes extension.
- Apply those activation defaults once at thread startup when the user has not explicitly configured token budgeting, while respecting managed feature requirements.
- Continue resolving model-owned prompts for the active model so model switches can use their own token-budget guidance.

## Testing

- Update the token-budget integration test to verify that model metadata activates the feature without manual configuration.

GitOrigin-RevId: 73a32623eaeabb9243e240c43da7a5e7e93e9a19
This commit is contained in:
pmccrary-oai
2026-08-31 08:31:23 +00:00
committed by copyberry
parent a9519cbcdd
commit d58d0e5841
8 changed files with 60 additions and 12 deletions
+4
View File
@@ -668,6 +668,10 @@ impl Session {
let model_info = models_manager
.get_model_info(model.as_str(), &config.to_models_manager_config())
.await;
// Intentionally resolve `enabled` and `use_history_notes_extension` only at
// thread startup. Both activation flags stay fixed for this thread runtime,
// even if the selected model changes later.
token_budget::apply_model_defaults(Arc::make_mut(&mut config), &model_info);
let configured_config = Arc::clone(&config);
let multi_agent_version = config.multi_agent_version_override().or_else(|| {
resolve_multi_agent_version(&conversation_history, inherited_multi_agent_version)
@@ -277,6 +277,8 @@ async fn submitted_sparse_updates_preserve_captured_steps_and_ordering() {
.as_mut()
.expect("model messages")
.token_budget = Some(ModelTokenBudgetConfig {
enabled: false,
use_history_notes_extension: false,
reminder_threshold_tokens: 2_000,
reminder_message_template: "{n_remaining} tokens remain.".to_string(),
guidance_message: format!("Guidance for {}.", model.slug),
+39
View File
@@ -68,6 +68,45 @@ pub(super) fn resolve_token_budget(
Some(token_budget)
}
/// Applies model activation defaults before thread extensions are initialized.
pub(super) fn apply_model_defaults(config: &mut Config, model_info: &ModelInfo) {
let Some(model_defaults) = model_info
.model_messages
.as_ref()
.and_then(|messages| messages.token_budget.as_ref())
else {
return;
};
if !model_defaults.enabled {
return;
}
let has_explicit_config = config.token_budget.is_some()
|| config
.config_layer_stack
.effective_config()
.get("features")
.and_then(|features| features.get("token_budget"))
.is_some();
if has_explicit_config {
return;
}
if config.features.enable(Feature::TokenBudget).is_err() {
return;
}
// Managed requirements can pin the feature off even when enable() succeeds.
if !config.features.enabled(Feature::TokenBudget) {
return;
}
// Keep prompts unresolved so later turns and model switches use their own defaults.
config.token_budget = Some(TokenBudgetConfig {
use_history_notes_extension: model_defaults.use_history_notes_extension,
..TokenBudgetConfig::default()
});
}
pub(super) async fn maybe_record(
sess: &Session,
turn_context: &TurnContext,
+2
View File
@@ -582,6 +582,8 @@ async fn review_uses_updated_turn_permissions_and_approval_policy() {
fn model_defaults(guidance_message: &str) -> ModelTokenBudgetConfig {
ModelTokenBudgetConfig {
enabled: false,
use_history_notes_extension: false,
reminder_threshold_tokens: 6_144,
reminder_message_template: "Reminder: {n_remaining} tokens remain.".to_string(),
guidance_message: guidance_message.to_string(),
@@ -430,6 +430,8 @@ async fn active_model_switch_resolves_token_budget_from_original_preferences(
.as_mut()
.expect("model messages")
.token_budget = (initial_model || destination_has_guidance).then(|| ModelTokenBudgetConfig {
enabled: false,
use_history_notes_extension: false,
reminder_threshold_tokens: if initial_model { 8_000 } else { 2_000 },
reminder_message_template: format!(
"Reminder for {slug}: {{n_remaining}} tokens remain."
+5 -12
View File
@@ -63,6 +63,8 @@ const AUTO_COMPACT_FALLBACK_PROMPT: &str = "Save the important state before roll
fn model_token_budget_config() -> ModelTokenBudgetConfig {
ModelTokenBudgetConfig {
enabled: false,
use_history_notes_extension: false,
reminder_threshold_tokens: 6_144,
reminder_message_template: "Model reminder: {n_remaining} tokens remain.".to_string(),
guidance_message: "Use the model-owned context-window guidance.".to_string(),
@@ -295,16 +297,11 @@ async fn token_budget_uses_model_message_defaults() -> Result<()> {
let server = start_mock_server().await;
let response = mount_sse_once(&server, sse_completed("resp-1")).await;
let model_defaults = model_token_budget_config();
let mut model_defaults = model_token_budget_config();
model_defaults.enabled = true;
model_defaults.use_history_notes_extension = true;
let expected_guidance = model_defaults.guidance_message.clone();
let test = test_codex()
.with_pre_build_hook(|home| {
std::fs::write(
home.join("config.toml"),
"[features.token_budget]\nenabled = true\nuse_history_notes_extension = true\n",
)
.expect("write token-budget configuration");
})
.with_model_info_override("gpt-5.2", move |model_info| {
model_info
.model_messages
@@ -314,10 +311,6 @@ async fn token_budget_uses_model_message_defaults() -> Result<()> {
})
.with_config(|config| {
config.model_context_window = Some(CONFIGURED_CONTEXT_WINDOW);
config
.features
.enable(Feature::TokenBudget)
.expect("test config should allow token budget");
})
.build_with_auto_env(&server)
.await?;
@@ -62,6 +62,8 @@ fn base_instruction_override_is_literal_and_preserves_catalog_messages() {
}),
};
let token_budget = ModelTokenBudgetConfig {
enabled: false,
use_history_notes_extension: false,
reminder_threshold_tokens: 128,
reminder_message_template: "budget reminder".to_string(),
guidance_message: "budget guidance".to_string(),
+4
View File
@@ -594,6 +594,10 @@ pub struct ToolMessage {
/// Model-owned defaults for the context-window token-budget feature.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, TS, JsonSchema)]
pub struct ModelTokenBudgetConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub use_history_notes_extension: bool,
pub reminder_threshold_tokens: i64,
pub reminder_message_template: String,
pub guidance_message: String,