Files
codex/codex-rs/core/src/compact_model_fallback.rs
T
pakrym-oai 8571b9eaa4 Install full context in compaction replacement history (#51117)
## Why

Manual, pre-turn, and post-turn compaction previously deferred rebuilding initial context until the next turn. Their saved replacement history could omit refreshed instructions and skill catalogs.

## What changed

- Install initial context during local and remote compaction, before the last real user message or summary, keeping the compaction summary last.
- Persist matching full world-state and turn-context baselines with every replacement instead of retaining only partial extension metadata.
- Build replacement context and recompute token usage for the current model even when the previous model performs compaction. Reuse that captured context for eligible current-model retries.
- Capture manual compaction context centrally and propagate preparation errors to the task runner.

## Testing

Update assertions and snapshots for context ordering, refreshed instructions, resume and fork history, current-model checkpoint baselines, and cloud skill catalog retention across compaction and resume.

GitOrigin-RevId: 8572a76c31c6884a3c580b3bbb350d49af876bd1
2026-10-05 16:10:27 +00:00

76 lines
2.4 KiB
Rust

use crate::session::turn_context::TurnContext;
use codex_analytics::CompactionImplementation;
use codex_analytics::CompactionReason;
use codex_otel::SessionTelemetry;
use codex_protocol::error::CodexErr;
use codex_protocol::error::CodexErrorDetails;
use tracing::warn;
/// Returns whether a failed compaction attempt should use the current model.
pub(crate) fn should_retry_with_current_model(
error: &CodexErr,
previous: &TurnContext,
current: &TurnContext,
) -> bool {
if matches!(
error.details(),
CodexErrorDetails::TurnAborted
| CodexErrorDetails::Interrupted
| CodexErrorDetails::SessionBudgetExceeded
) || (previous.model_info().slug == current.model_info().slug
&& previous.cyber_access_program == current.cyber_access_program)
{
return false;
}
current.provider.info().is_openai()
&& current
.auth_manager
.as_deref()
.and_then(codex_login::AuthManager::auth_cached)
.is_some_and(|auth| auth.uses_codex_backend() || auth.is_api_key_auth())
}
pub(crate) fn record_model_fallback(
session_telemetry: &SessionTelemetry,
previous_model: &str,
current_model: &str,
reason: CompactionReason,
implementation: CompactionImplementation,
fallback_error: Option<&CodexErr>,
) {
let reason_tag = match reason {
CompactionReason::UserRequested => "user_requested",
CompactionReason::ContextLimit => "context_limit",
CompactionReason::ModelDownshift => "model_downshift",
CompactionReason::CompHashChanged => "comp_hash_changed",
};
let implementation_tag = match implementation {
CompactionImplementation::Responses => "responses",
CompactionImplementation::ResponsesCompactionV2 => "responses_compaction_v2",
};
let outcome = if fallback_error.is_none() {
"succeeded"
} else {
"failed"
};
session_telemetry.counter(
"codex.compaction.model_fallback",
/*inc*/ 1,
&[
("reason", reason_tag),
("implementation", implementation_tag),
("outcome", outcome),
],
);
warn!(
previous_model,
current_model,
?reason,
?implementation,
outcome,
?fallback_error,
"previous-model compaction failed; retried with current model"
);
}