mirror of
https://github.com/openai/codex.git
synced 2026-11-11 21:10:39 +00:00
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:
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user