mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Centralize context update diffing logic (#11807)
## Summary This PR centralizes model-visible state diffing for turn context updates into one module, while keeping existing behavior and call sites stable. ### What changed - Added `core/src/context_updates.rs` with the consolidated diffing logic for: - environment context updates - permissions/policy updates - collaboration mode updates - model-instruction switch updates - personality updates - Added `BuildSettingsUpdateItemsParams` so required dependencies are passed explicitly. - Updated `Session::build_settings_update_items` in `core/src/codex.rs` to delegate to the centralized module. - Reused the same centralized `personality_message_for` helper from initial-context assembly to avoid duplicated logic. - Registered the new module in `core/src/lib.rs`. ## Why This is a minimal, shippable step toward the model-visible-state design: all state diff decisions for turn-context update items now live in one place, improving reviewability and reducing drift risk without expanding scope. ## Behavior - Intended to be behavior-preserving. - No protocol/schema changes. - No call-site behavior changes beyond routing through the new centralized logic. ## Testing Ran targeted tests in this worktree: - `cargo test -p codex-core build_settings_update_items_emits_environment_item_for_network_changes` - `cargo test -p codex-core collaboration_instructions --test all` Both passed. ## Codex author `codex resume 019c540f-3951-7352-a3fa-6f07b834d4ce`
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
mod history;
|
||||
mod normalize;
|
||||
pub(crate) mod updates;
|
||||
|
||||
pub(crate) use history::ContextManager;
|
||||
pub(crate) use history::TotalTokenUsageBreakdown;
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
use crate::codex::TurnContext;
|
||||
use crate::environment_context::EnvironmentContext;
|
||||
use crate::shell::Shell;
|
||||
use codex_execpolicy::Policy;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::models::DeveloperInstructions;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
|
||||
fn build_environment_update_item(
|
||||
previous: Option<&TurnContext>,
|
||||
next: &TurnContext,
|
||||
shell: &Shell,
|
||||
) -> Option<ResponseItem> {
|
||||
let prev = previous?;
|
||||
let prev_context = EnvironmentContext::from_turn_context(prev, shell);
|
||||
let next_context = EnvironmentContext::from_turn_context(next, shell);
|
||||
if prev_context.equals_except_shell(&next_context) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(ResponseItem::from(EnvironmentContext::diff(
|
||||
prev, next, shell,
|
||||
)))
|
||||
}
|
||||
|
||||
fn build_permissions_update_item(
|
||||
previous: Option<&TurnContext>,
|
||||
next: &TurnContext,
|
||||
exec_policy: &Policy,
|
||||
) -> Option<ResponseItem> {
|
||||
let prev = previous?;
|
||||
if prev.sandbox_policy == next.sandbox_policy && prev.approval_policy == next.approval_policy {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(
|
||||
DeveloperInstructions::from_policy(
|
||||
&next.sandbox_policy,
|
||||
next.approval_policy,
|
||||
exec_policy,
|
||||
&next.cwd,
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn build_collaboration_mode_update_item(
|
||||
previous: Option<&TurnContext>,
|
||||
next: &TurnContext,
|
||||
) -> Option<ResponseItem> {
|
||||
let prev = previous?;
|
||||
if prev.collaboration_mode != next.collaboration_mode {
|
||||
// If the next mode has empty developer instructions, this returns None and we emit no
|
||||
// update, so prior collaboration instructions remain in the prompt history.
|
||||
Some(DeveloperInstructions::from_collaboration_mode(&next.collaboration_mode)?.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn build_personality_update_item(
|
||||
previous: Option<&TurnContext>,
|
||||
next: &TurnContext,
|
||||
personality_feature_enabled: bool,
|
||||
) -> Option<ResponseItem> {
|
||||
if !personality_feature_enabled {
|
||||
return None;
|
||||
}
|
||||
let previous = previous?;
|
||||
if next.model_info.slug != previous.model_info.slug {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(personality) = next.personality
|
||||
&& next.personality != previous.personality
|
||||
{
|
||||
let model_info = &next.model_info;
|
||||
let personality_message = personality_message_for(model_info, personality);
|
||||
personality_message
|
||||
.map(|message| DeveloperInstructions::personality_spec_message(message).into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn personality_message_for(
|
||||
model_info: &ModelInfo,
|
||||
personality: Personality,
|
||||
) -> Option<String> {
|
||||
model_info
|
||||
.model_messages
|
||||
.as_ref()
|
||||
.and_then(|spec| spec.get_personality_message(Some(personality)))
|
||||
.filter(|message| !message.is_empty())
|
||||
}
|
||||
|
||||
pub(crate) fn build_model_instructions_update_item(
|
||||
previous: Option<&TurnContext>,
|
||||
resumed_model: Option<&str>,
|
||||
next: &TurnContext,
|
||||
) -> Option<ResponseItem> {
|
||||
let previous_model =
|
||||
resumed_model.or_else(|| previous.map(|prev| prev.model_info.slug.as_str()))?;
|
||||
if previous_model == next.model_info.slug {
|
||||
return None;
|
||||
}
|
||||
|
||||
let model_instructions = next.model_info.get_model_instructions(next.personality);
|
||||
if model_instructions.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(DeveloperInstructions::model_switch_message(model_instructions).into())
|
||||
}
|
||||
|
||||
pub(crate) fn build_settings_update_items(
|
||||
previous: Option<&TurnContext>,
|
||||
resumed_model: Option<&str>,
|
||||
next: &TurnContext,
|
||||
shell: &Shell,
|
||||
exec_policy: &Policy,
|
||||
personality_feature_enabled: bool,
|
||||
) -> Vec<ResponseItem> {
|
||||
let mut update_items = Vec::new();
|
||||
|
||||
if let Some(env_item) = build_environment_update_item(previous, next, shell) {
|
||||
update_items.push(env_item);
|
||||
}
|
||||
if let Some(permissions_item) = build_permissions_update_item(previous, next, exec_policy) {
|
||||
update_items.push(permissions_item);
|
||||
}
|
||||
if let Some(collaboration_mode_item) = build_collaboration_mode_update_item(previous, next) {
|
||||
update_items.push(collaboration_mode_item);
|
||||
}
|
||||
if let Some(model_instructions_item) =
|
||||
build_model_instructions_update_item(previous, resumed_model, next)
|
||||
{
|
||||
update_items.push(model_instructions_item);
|
||||
}
|
||||
if let Some(personality_item) =
|
||||
build_personality_update_item(previous, next, personality_feature_enabled)
|
||||
{
|
||||
update_items.push(personality_item);
|
||||
}
|
||||
|
||||
update_items
|
||||
}
|
||||
Reference in New Issue
Block a user