mirror of
https://github.com/openai/codex.git
synced 2026-10-27 08:01:52 +00:00
Track model and personality in world state (#35530)
## What changed - Add model and personality sections to the persisted world-state snapshot. - Generate model-switch and personality instructions through world-state diffs, including when prior state must be inferred during replay. - Keep those instruction fragments in standalone developer messages instead of merging them with adjacent context updates. ## Testing - Cover initial, unchanged, and changed model and personality states. - Verify that model changes are recorded in rollout world-state items. GitOrigin-RevId: 497e29cf38cac08e992e546df900f6f8b01120f9
This commit is contained in:
@@ -26,8 +26,8 @@ use crate::config::resolve_tool_suggest_config_from_layer_stack;
|
||||
use crate::context::ApprovedCommandPrefixSaved;
|
||||
use crate::context::AvailableSkillsInstructions;
|
||||
use crate::context::ContextualUserFragment;
|
||||
use crate::context::ModelSwitchInstructions;
|
||||
use crate::context::NetworkRuleSaved;
|
||||
use crate::context::PersonalitySpecInstructions;
|
||||
use crate::context::RecommendedPluginsInstructions;
|
||||
use crate::context::world_state::WorldState;
|
||||
use crate::current_time::TimeProvider;
|
||||
@@ -1807,27 +1807,6 @@ impl Session {
|
||||
self.refresh_runtime_config(next_config).await;
|
||||
}
|
||||
|
||||
async fn build_settings_update_items(
|
||||
&self,
|
||||
reference_context_item: Option<&TurnContextItem>,
|
||||
current_context: &TurnContext,
|
||||
) -> Vec<ResponseItem> {
|
||||
// TODO: Make context updates a pure diff of persisted previous/current TurnContextItem
|
||||
// state so replay/backtracking is deterministic. Runtime inputs that affect model-visible
|
||||
// context (feature gates and the previous-turn bridge) should be persisted
|
||||
// state or explicit non-state replay events.
|
||||
let previous_turn_settings = {
|
||||
let state = self.state.lock().await;
|
||||
state.previous_turn_settings()
|
||||
};
|
||||
crate::context_manager::updates::build_settings_update_items(
|
||||
reference_context_item,
|
||||
previous_turn_settings.as_ref(),
|
||||
current_context,
|
||||
self.features.enabled(Feature::Personality),
|
||||
)
|
||||
}
|
||||
|
||||
/// Record a terminal CodexErr before the app-server completion notification is reduced.
|
||||
pub(crate) fn track_turn_codex_error(&self, turn_context: &TurnContext, error: &CodexErr) {
|
||||
self.services
|
||||
@@ -3351,23 +3330,13 @@ impl Session {
|
||||
let mut developer_sections = Vec::<String>::with_capacity(8);
|
||||
let mut contextual_user_sections = Vec::<String>::with_capacity(2);
|
||||
let mut separate_developer_sections = Vec::<String>::new();
|
||||
let (previous_turn_settings, base_instructions, session_source, auto_compact_window_ids) = {
|
||||
let (session_source, auto_compact_window_ids) = {
|
||||
let state = self.state.lock().await;
|
||||
(
|
||||
state.previous_turn_settings(),
|
||||
state.session_configuration.base_instructions.clone(),
|
||||
state.session_configuration.session_source.clone(),
|
||||
state.auto_compact_window_ids(),
|
||||
)
|
||||
};
|
||||
if let Some(model_switch_message) =
|
||||
crate::context_manager::updates::build_model_instructions_update_item(
|
||||
previous_turn_settings.as_ref(),
|
||||
turn_context,
|
||||
)
|
||||
{
|
||||
developer_sections.push(model_switch_message);
|
||||
}
|
||||
let separate_guardian_developer_message =
|
||||
crate::guardian::is_guardian_reviewer_source(&session_source);
|
||||
// Keep the guardian policy prompt out of the aggregated developer bundle so it
|
||||
@@ -3378,23 +3347,6 @@ impl Session {
|
||||
{
|
||||
developer_sections.push(developer_instructions.to_string());
|
||||
}
|
||||
if self.features.enabled(Feature::Personality)
|
||||
&& let Some(personality) = turn_context.personality
|
||||
{
|
||||
let model_info = turn_context.model_info.clone();
|
||||
let has_baked_personality = model_info.supports_personality()
|
||||
&& base_instructions == model_info.get_model_instructions(Some(personality));
|
||||
if !has_baked_personality
|
||||
&& let Some(personality_message) =
|
||||
crate::context_manager::updates::personality_message_for(
|
||||
&model_info,
|
||||
personality,
|
||||
)
|
||||
{
|
||||
developer_sections
|
||||
.push(PersonalitySpecInstructions::new(personality_message).render());
|
||||
}
|
||||
}
|
||||
if turn_context.config.include_skill_instructions {
|
||||
let host_catalog_in_world_state = turn_context
|
||||
.extension_data
|
||||
@@ -3546,6 +3498,12 @@ impl Session {
|
||||
let mut initial_multi_agent_mode = None;
|
||||
for fragment in world_state.render_full() {
|
||||
match fragment.role() {
|
||||
"developer"
|
||||
if fragment.markers().0 == ModelSwitchInstructions::type_markers().0 =>
|
||||
{
|
||||
// New-model instructions must precede the rest of the developer context.
|
||||
developer_sections.insert(0, fragment.render());
|
||||
}
|
||||
"developer" if fragment.markers().0 == MULTI_AGENT_MODE_OPEN_TAG => {
|
||||
initial_multi_agent_mode = Some(fragment);
|
||||
}
|
||||
@@ -3720,11 +3678,6 @@ impl Session {
|
||||
Some(WorldStateItem::full(snapshot.into_value())),
|
||||
)
|
||||
} else {
|
||||
// Steady-state path: append only built-in context diffs here; turn-scoped extension
|
||||
// context is added below.
|
||||
let mut context_items = self
|
||||
.build_settings_update_items(reference_context_item.as_ref(), turn_context)
|
||||
.await;
|
||||
let (world_state_items, world_state_item) = {
|
||||
let mut state = self.state.lock().await;
|
||||
let (fragments, rollout_item) =
|
||||
@@ -3734,8 +3687,7 @@ impl Session {
|
||||
rollout_item,
|
||||
)
|
||||
};
|
||||
context_items.extend(world_state_items);
|
||||
(context_items, world_state_item)
|
||||
(world_state_items, world_state_item)
|
||||
};
|
||||
if !should_inject_full_context && turn_context_changed {
|
||||
context_items.extend(
|
||||
|
||||
@@ -9359,11 +9359,6 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
}
|
||||
let rollout_path = attach_thread_persistence(&mut session).await;
|
||||
|
||||
let update_items = session
|
||||
.build_settings_update_items(Some(&previous_context_item), &turn_context)
|
||||
.await;
|
||||
assert_eq!(update_items, Vec::new());
|
||||
|
||||
let turn_context = Arc::new(turn_context);
|
||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||
session
|
||||
|
||||
@@ -7,8 +7,10 @@ use crate::context::world_state::AppsInstructionsState;
|
||||
use crate::context::world_state::CollaborationModeState;
|
||||
use crate::context::world_state::EnvironmentsInstructionsState;
|
||||
use crate::context::world_state::EnvironmentsState;
|
||||
use crate::context::world_state::ModelInstructionsState;
|
||||
use crate::context::world_state::MultiAgentModeState;
|
||||
use crate::context::world_state::PermissionsState;
|
||||
use crate::context::world_state::PersonalityState;
|
||||
use crate::context::world_state::PluginsInstructionsState;
|
||||
use crate::context::world_state::RealtimeState;
|
||||
use crate::context::world_state::ToolsState;
|
||||
@@ -27,6 +29,21 @@ impl Session {
|
||||
selected_capability_root_count = step_context.selected_capability_roots.len(),
|
||||
"building step world state"
|
||||
);
|
||||
let (previous_model, previous_context, base_instructions) = {
|
||||
let state = self.state.lock().await;
|
||||
(
|
||||
state
|
||||
.previous_turn_settings()
|
||||
.map(|previous| previous.model),
|
||||
state.reference_context_item(),
|
||||
state.session_configuration.base_instructions.clone(),
|
||||
)
|
||||
};
|
||||
let model_instructions = turn_context
|
||||
.model_info
|
||||
.get_model_instructions(turn_context.personality);
|
||||
let personality_is_baked = turn_context.model_info.supports_personality()
|
||||
&& base_instructions == model_instructions;
|
||||
let environment_subagents = if turn_context.config.include_environment_context {
|
||||
self.services
|
||||
.agent_control
|
||||
@@ -36,6 +53,33 @@ impl Session {
|
||||
String::new()
|
||||
};
|
||||
let mut world_state = WorldState::default();
|
||||
world_state.add_section(ModelInstructionsState::new(
|
||||
&turn_context.model_info.slug,
|
||||
previous_model.as_deref(),
|
||||
model_instructions,
|
||||
));
|
||||
if self.features.enabled(Feature::Personality) {
|
||||
let personality_instructions = turn_context.personality.and_then(|personality| {
|
||||
turn_context
|
||||
.model_info
|
||||
.model_messages
|
||||
.as_ref()
|
||||
.and_then(|messages| messages.get_personality_message(Some(personality)))
|
||||
.filter(|message| !message.is_empty())
|
||||
});
|
||||
world_state.add_section(PersonalityState::new(
|
||||
&turn_context.model_info.slug,
|
||||
turn_context.personality,
|
||||
previous_context
|
||||
.as_ref()
|
||||
.map(|previous| previous.model.as_str()),
|
||||
previous_context
|
||||
.as_ref()
|
||||
.and_then(|previous| previous.personality),
|
||||
personality_instructions,
|
||||
personality_is_baked,
|
||||
));
|
||||
}
|
||||
world_state.add_section(RealtimeState::new(
|
||||
turn_context.realtime_active,
|
||||
turn_context
|
||||
|
||||
Reference in New Issue
Block a user