mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Fix remote compaction estimator/payload instruction small mismatch (#10692)
## Summary This PR fixes a deterministic mismatch in remote compaction where pre-trim estimation and the `/v1/responses/compact` payload could use different base instructions. Before this change: - pre-trim estimation used model-derived instructions (`model_info.get_model_instructions(...)`) - compact payload used session base instructions (`sess.get_base_instructions()`) After this change: - remote pre-trim estimation and compact payload both use the same `BaseInstructions` instance from session state. ## Changes - Added a shared estimator entry point in `ContextManager`: - `estimate_token_count_with_base_instructions(&self, base_instructions: &BaseInstructions) -> Option<i64>` - Kept `estimate_token_count(&TurnContext)` as a thin wrapper that resolves model/personality instructions and delegates to the new helper. - Updated remote compaction flow to fetch base instructions once and reuse it for both: - trim preflight estimation - compact request payload construction - Added regression coverage for parity and behavior: - unit test verifying explicit-base estimator behavior - integration test proving remote compaction uses session override instructions and trims accordingly ## Why this matters This removes a deterministic divergence source where pre-trim could think the request fits while the actual compact request exceeded context because its instructions were longer/different. ## Scope In scope: - estimator/payload base-instructions parity in remote compaction Out of scope: - retry-on-`context_length_exceeded` - compaction threshold/headroom policy changes - broader trimming policy changes ## Codex author: `codex fork 019c2b24-c2df-7b31-a482-fb8cf7a28559`
This commit is contained in:
@@ -2117,10 +2117,10 @@ impl Session {
|
||||
}
|
||||
|
||||
pub(crate) async fn recompute_token_usage(&self, turn_context: &TurnContext) {
|
||||
let Some(estimated_total_tokens) = self
|
||||
.clone_history()
|
||||
.await
|
||||
.estimate_token_count(turn_context)
|
||||
let history = self.clone_history().await;
|
||||
let base_instructions = self.get_base_instructions().await;
|
||||
let Some(estimated_total_tokens) =
|
||||
history.estimate_token_count_with_base_instructions(&base_instructions)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -4782,6 +4782,7 @@ mod tests {
|
||||
use crate::turn_diff_tracker::TurnDiffTracker;
|
||||
use codex_app_server_protocol::AppInfo;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use std::path::Path;
|
||||
@@ -5061,6 +5062,46 @@ mod tests {
|
||||
assert_eq!(actual, Some(info2));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn recompute_token_usage_uses_session_base_instructions() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
|
||||
let override_instructions = "SESSION_OVERRIDE_INSTRUCTIONS_ONLY".repeat(120);
|
||||
{
|
||||
let mut state = session.state.lock().await;
|
||||
state.session_configuration.base_instructions = override_instructions.clone();
|
||||
}
|
||||
|
||||
let item = user_message("hello");
|
||||
session
|
||||
.record_into_history(std::slice::from_ref(&item), &turn_context)
|
||||
.await;
|
||||
|
||||
let history = session.clone_history().await;
|
||||
let session_base_instructions = BaseInstructions {
|
||||
text: override_instructions,
|
||||
};
|
||||
let expected_tokens = history
|
||||
.estimate_token_count_with_base_instructions(&session_base_instructions)
|
||||
.expect("estimate with session base instructions");
|
||||
let model_estimated_tokens = history
|
||||
.estimate_token_count(&turn_context)
|
||||
.expect("estimate with model instructions");
|
||||
assert_ne!(expected_tokens, model_estimated_tokens);
|
||||
|
||||
session.recompute_token_usage(&turn_context).await;
|
||||
|
||||
let actual_tokens = session
|
||||
.state
|
||||
.lock()
|
||||
.await
|
||||
.token_info()
|
||||
.expect("token info")
|
||||
.last_token_usage
|
||||
.total_tokens;
|
||||
assert_eq!(actual_tokens, expected_tokens.max(0));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn record_initial_history_reconstructs_forked_transcript() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
|
||||
Reference in New Issue
Block a user