Honor null thread instructions (#16964)

- Treat explicit null thread instructions as a blank-slate override
while preserving omitted-field fallback behavior.
- Preserve null through rollout resume/fork and keep explicit empty
strings distinct.
- Add app-server v2 start/fork coverage for the tri-state instruction
params.
This commit is contained in:
Ahmed Ibrahim
2026-04-06 21:10:19 -07:00
committed by GitHub
parent 4bb507d2c4
commit 24c598e8a9
39 changed files with 550 additions and 101 deletions

View File

@@ -581,11 +581,15 @@ impl Codex {
let model_info = models_manager
.get_model_info(model.as_str(), &config.to_models_manager_config())
.await;
let base_instructions = config
.base_instructions
.clone()
.or_else(|| conversation_history.get_base_instructions().map(|s| s.text))
.unwrap_or_else(|| model_info.get_model_instructions(config.personality));
let base_instructions = match config.base_instructions.clone() {
Some(base_instructions) => base_instructions,
None => conversation_history
.get_base_instructions()
.map(|base_instructions| {
base_instructions.map(|base_instructions| base_instructions.text)
})
.unwrap_or_else(|| Some(model_info.get_model_instructions(config.personality))),
};
// Respect thread-start tools. When missing (resumed/forked threads), read from the db
// first, then fall back to rollout-file tools.
@@ -1106,7 +1110,7 @@ pub(crate) struct SessionConfiguration {
personality: Option<Personality>,
/// Base instructions for the session.
base_instructions: String,
base_instructions: Option<String>,
/// Compact prompt override.
compact_prompt: Option<String>,
@@ -1545,9 +1549,10 @@ impl Session {
conversation_id,
forked_from_id,
session_source,
BaseInstructions {
text: session_configuration.base_instructions.clone(),
},
session_configuration
.base_instructions
.clone()
.map(|text| BaseInstructions { text }),
session_configuration.dynamic_tools.clone(),
if session_configuration.persist_extended_history {
EventPersistenceMode::Extended
@@ -2109,8 +2114,9 @@ impl Session {
));
}
}
sess.schedule_startup_prewarm(session_configuration.base_instructions.clone())
.await;
if let Some(base_instructions) = session_configuration.base_instructions.clone() {
sess.schedule_startup_prewarm(base_instructions).await;
}
let session_start_source = match &initial_history {
InitialHistory::Resumed(_) => codex_hooks::SessionStartSource::Resume,
InitialHistory::New | InitialHistory::Forked(_) => {
@@ -2212,11 +2218,13 @@ impl Session {
state.history.estimate_token_count(turn_context)
}
pub(crate) async fn get_base_instructions(&self) -> BaseInstructions {
pub(crate) async fn get_base_instructions(&self) -> Option<BaseInstructions> {
let state = self.state.lock().await;
BaseInstructions {
text: state.session_configuration.base_instructions.clone(),
}
state
.session_configuration
.base_instructions
.clone()
.map(|text| BaseInstructions { text })
}
// Merges connector IDs into the session-level explicit connector selection.
@@ -3620,7 +3628,11 @@ impl Session {
state.reference_context_item(),
state.previous_turn_settings(),
state.session_configuration.collaboration_mode.clone(),
state.session_configuration.base_instructions.clone(),
state
.session_configuration
.base_instructions
.clone()
.unwrap_or_default(),
state.session_configuration.session_source.clone(),
)
};
@@ -3861,7 +3873,13 @@ impl Session {
pub(crate) async fn recompute_token_usage(&self, turn_context: &TurnContext) {
let history = self.clone_history().await;
let base_instructions = self.get_base_instructions().await;
let empty_base_instructions = BaseInstructions {
text: String::new(),
};
let base_instructions = self
.get_base_instructions()
.await
.unwrap_or(empty_base_instructions);
let Some(estimated_total_tokens) =
history.estimate_token_count_with_base_instructions(&base_instructions)
else {
@@ -6555,7 +6573,7 @@ pub(crate) fn build_prompt(
input: Vec<ResponseItem>,
router: &ToolRouter,
turn_context: &TurnContext,
base_instructions: BaseInstructions,
base_instructions: Option<BaseInstructions>,
) -> Prompt {
let deferred_dynamic_tools = turn_context
.dynamic_tools