mirror of
https://github.com/openai/codex.git
synced 2026-05-02 20:32:04 +03:00
Account for encrypted reasoning for auto compaction (#7113)
- The total token used returned from the api doesn't account for the reasoning items before the assistant message - Account for those for auto compaction - Add the encrypted reasoning effort in the common tests utils - Add a test to make sure it works as expected
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::codex::TurnContext;
|
||||
use crate::context_manager::normalize;
|
||||
use crate::truncate::TruncationPolicy;
|
||||
use crate::truncate::approx_token_count;
|
||||
use crate::truncate::approx_tokens_from_byte_count;
|
||||
use crate::truncate::truncate_function_output_items_with_policy;
|
||||
use crate::truncate::truncate_text;
|
||||
use codex_protocol::models::FunctionCallOutputPayload;
|
||||
@@ -119,6 +120,54 @@ impl ContextManager {
|
||||
);
|
||||
}
|
||||
|
||||
fn get_non_last_reasoning_items_tokens(&self) -> usize {
|
||||
// get reasoning items excluding all the ones after the last user message
|
||||
let Some(last_user_index) = self
|
||||
.items
|
||||
.iter()
|
||||
.rposition(|item| matches!(item, ResponseItem::Message { role, .. } if role == "user"))
|
||||
else {
|
||||
return 0usize;
|
||||
};
|
||||
|
||||
let total_reasoning_bytes = self
|
||||
.items
|
||||
.iter()
|
||||
.take(last_user_index)
|
||||
.filter_map(|item| {
|
||||
if let ResponseItem::Reasoning {
|
||||
encrypted_content: Some(content),
|
||||
..
|
||||
} = item
|
||||
{
|
||||
Some(content.len())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(Self::estimate_reasoning_length)
|
||||
.fold(0usize, usize::saturating_add);
|
||||
|
||||
let token_estimate = approx_tokens_from_byte_count(total_reasoning_bytes);
|
||||
token_estimate as usize
|
||||
}
|
||||
|
||||
fn estimate_reasoning_length(encoded_len: usize) -> usize {
|
||||
encoded_len
|
||||
.saturating_mul(3)
|
||||
.checked_div(4)
|
||||
.unwrap_or(0)
|
||||
.saturating_sub(650)
|
||||
}
|
||||
|
||||
pub(crate) fn get_total_token_usage(&self) -> i64 {
|
||||
self.token_info
|
||||
.as_ref()
|
||||
.map(|info| info.last_token_usage.total_tokens)
|
||||
.unwrap_or(0)
|
||||
.saturating_add(self.get_non_last_reasoning_items_tokens() as i64)
|
||||
}
|
||||
|
||||
/// This function enforces a couple of invariants on the in-memory history:
|
||||
/// 1. every call (function/custom) has a corresponding output entry
|
||||
/// 2. every output has a corresponding call entry
|
||||
|
||||
@@ -56,6 +56,17 @@ fn reasoning_msg(text: &str) -> ResponseItem {
|
||||
}
|
||||
}
|
||||
|
||||
fn reasoning_with_encrypted_content(len: usize) -> ResponseItem {
|
||||
ResponseItem::Reasoning {
|
||||
id: String::new(),
|
||||
summary: vec![ReasoningItemReasoningSummary::SummaryText {
|
||||
text: "summary".to_string(),
|
||||
}],
|
||||
content: None,
|
||||
encrypted_content: Some("a".repeat(len)),
|
||||
}
|
||||
}
|
||||
|
||||
fn truncate_exec_output(content: &str) -> String {
|
||||
truncate::truncate_text(content, TruncationPolicy::Tokens(EXEC_FORMAT_MAX_TOKENS))
|
||||
}
|
||||
@@ -112,6 +123,28 @@ fn filters_non_api_messages() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_last_reasoning_tokens_return_zero_when_no_user_messages() {
|
||||
let history = create_history_with_items(vec![reasoning_with_encrypted_content(800)]);
|
||||
|
||||
assert_eq!(history.get_non_last_reasoning_items_tokens(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_last_reasoning_tokens_ignore_entries_after_last_user() {
|
||||
let history = create_history_with_items(vec![
|
||||
reasoning_with_encrypted_content(900),
|
||||
user_msg("first"),
|
||||
reasoning_with_encrypted_content(1_000),
|
||||
user_msg("second"),
|
||||
reasoning_with_encrypted_content(2_000),
|
||||
]);
|
||||
// first: (900 * 0.75 - 650) / 4 = 6.25 tokens
|
||||
// second: (1000 * 0.75 - 650) / 4 = 25 tokens
|
||||
// first + second = 62.5
|
||||
assert_eq!(history.get_non_last_reasoning_items_tokens(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_history_for_prompt_drops_ghost_commits() {
|
||||
let items = vec![ResponseItem::GhostSnapshot {
|
||||
|
||||
Reference in New Issue
Block a user