mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
Budget realtime current thread context (#17519)
Select Current Thread startup context by budget from newest turns, cap each rendered turn at 300 approximate tokens, and add formatter plus integration snapshot coverage.
This commit is contained in:
@@ -15,6 +15,7 @@ use std::collections::HashSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::DirEntry;
|
||||
use std::io;
|
||||
use std::mem::take;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tracing::debug;
|
||||
@@ -26,7 +27,7 @@ const CURRENT_THREAD_SECTION_TOKEN_BUDGET: usize = 1_200;
|
||||
const RECENT_WORK_SECTION_TOKEN_BUDGET: usize = 2_200;
|
||||
const WORKSPACE_SECTION_TOKEN_BUDGET: usize = 1_600;
|
||||
const NOTES_SECTION_TOKEN_BUDGET: usize = 300;
|
||||
const MAX_CURRENT_THREAD_TURNS: usize = 2;
|
||||
const CURRENT_THREAD_TURN_TOKEN_BUDGET: usize = 300;
|
||||
const MAX_RECENT_THREADS: usize = 40;
|
||||
const MAX_RECENT_WORK_GROUPS: usize = 8;
|
||||
const MAX_CURRENT_CWD_ASKS: usize = 8;
|
||||
@@ -204,10 +205,7 @@ fn build_current_thread_section(items: &[ResponseItem]) -> Option<String> {
|
||||
continue;
|
||||
};
|
||||
if !current_user.is_empty() || !current_assistant.is_empty() {
|
||||
turns.push((
|
||||
std::mem::take(&mut current_user),
|
||||
std::mem::take(&mut current_assistant),
|
||||
));
|
||||
turns.push((take(&mut current_user), take(&mut current_assistant)));
|
||||
}
|
||||
current_user.push(text);
|
||||
}
|
||||
@@ -231,43 +229,75 @@ fn build_current_thread_section(items: &[ResponseItem]) -> Option<String> {
|
||||
turns.push((current_user, current_assistant));
|
||||
}
|
||||
|
||||
let retained_turns = turns
|
||||
.into_iter()
|
||||
.rev()
|
||||
.take(MAX_CURRENT_THREAD_TURNS)
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.rev()
|
||||
.collect::<Vec<_>>();
|
||||
if retained_turns.is_empty() {
|
||||
if turns.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut lines = vec![
|
||||
"Most recent user/assistant turns from this exact thread. Use them for continuity when responding.".to_string(),
|
||||
];
|
||||
let mut remaining_budget =
|
||||
CURRENT_THREAD_SECTION_TOKEN_BUDGET.saturating_sub(approx_token_count(&lines.join("\n")));
|
||||
let mut retained_turn_count = 0;
|
||||
|
||||
let retained_turn_count = retained_turns.len();
|
||||
for (index, (user_messages, assistant_messages)) in retained_turns.into_iter().enumerate() {
|
||||
lines.push(String::new());
|
||||
if retained_turn_count == 1 || index + 1 == retained_turn_count {
|
||||
lines.push("### Latest turn".to_string());
|
||||
for (index, (user_messages, assistant_messages)) in turns.into_iter().rev().enumerate() {
|
||||
if remaining_budget == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut turn_lines = Vec::new();
|
||||
if index == 0 {
|
||||
turn_lines.push("### Latest turn".to_string());
|
||||
} else {
|
||||
lines.push(format!("### Prior turn {}", index + 1));
|
||||
turn_lines.push(format!("### Previous turn {index}"));
|
||||
}
|
||||
|
||||
if !user_messages.is_empty() {
|
||||
lines.push("User:".to_string());
|
||||
lines.push(user_messages.join("\n\n"));
|
||||
turn_lines.push("User:".to_string());
|
||||
turn_lines.push(user_messages.join("\n\n"));
|
||||
}
|
||||
if !assistant_messages.is_empty() {
|
||||
lines.push(String::new());
|
||||
lines.push("Assistant:".to_string());
|
||||
lines.push(assistant_messages.join("\n\n"));
|
||||
turn_lines.push(String::new());
|
||||
turn_lines.push("Assistant:".to_string());
|
||||
turn_lines.push(assistant_messages.join("\n\n"));
|
||||
}
|
||||
|
||||
let turn_budget = CURRENT_THREAD_TURN_TOKEN_BUDGET.min(remaining_budget);
|
||||
let turn_text = turn_lines.join("\n");
|
||||
let mut truncation_budget = turn_budget;
|
||||
let turn_text = loop {
|
||||
let candidate = truncate_text(&turn_text, TruncationPolicy::Tokens(truncation_budget));
|
||||
let candidate_tokens = approx_token_count(&candidate);
|
||||
if candidate_tokens <= turn_budget {
|
||||
break candidate;
|
||||
}
|
||||
|
||||
// The shared truncator adds its marker after choosing preserved
|
||||
// content, so tighten the content budget until the rendered turn
|
||||
// itself fits the per-turn cap.
|
||||
let excess_tokens = candidate_tokens.saturating_sub(turn_budget);
|
||||
let next_budget = truncation_budget.saturating_sub(excess_tokens.max(1));
|
||||
if next_budget == 0 {
|
||||
let candidate = truncate_text(&turn_text, TruncationPolicy::Tokens(0));
|
||||
if approx_token_count(&candidate) <= turn_budget {
|
||||
break candidate;
|
||||
}
|
||||
break String::new();
|
||||
}
|
||||
truncation_budget = next_budget;
|
||||
};
|
||||
let turn_tokens = approx_token_count(&turn_text);
|
||||
if turn_tokens == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
lines.push(String::new());
|
||||
lines.push(turn_text);
|
||||
remaining_budget = remaining_budget.saturating_sub(turn_tokens);
|
||||
retained_turn_count += 1;
|
||||
}
|
||||
|
||||
Some(lines.join("\n"))
|
||||
(retained_turn_count > 0).then(|| lines.join("\n"))
|
||||
}
|
||||
|
||||
fn build_workspace_section_with_user_root(
|
||||
|
||||
Reference in New Issue
Block a user