mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] allow disabling prompt instruction blocks (#16735)
This PR adds root and profile config switches to omit the generated `<permissions instructions>` and `<apps_instructions>` prompt blocks while keeping both enabled by default, and it gates both the initial developer-context injection and later permissions diff injection so turning the permissions block off stays effective across turn-context overrides. Also added a prompt debug tool that can be used as `codex debug prompt-input "hello"` and dumps the constructed items list.
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig;
|
||||
use codex_protocol::error::Result as CodexResult;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::codex::Session;
|
||||
use crate::codex::build_prompt;
|
||||
use crate::codex::built_tools;
|
||||
use crate::config::Config;
|
||||
use crate::thread_manager::ThreadManager;
|
||||
|
||||
/// Build the model-visible `input` list for a single debug turn.
|
||||
#[doc(hidden)]
|
||||
pub async fn build_prompt_input(
|
||||
mut config: Config,
|
||||
input: Vec<UserInput>,
|
||||
) -> CodexResult<Vec<ResponseItem>> {
|
||||
config.ephemeral = true;
|
||||
|
||||
let auth_manager = AuthManager::shared(
|
||||
config.codex_home.clone(),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
);
|
||||
auth_manager.set_forced_chatgpt_workspace_id(config.forced_chatgpt_workspace_id.clone());
|
||||
|
||||
let thread_manager = ThreadManager::new(
|
||||
&config,
|
||||
Arc::clone(&auth_manager),
|
||||
SessionSource::Exec,
|
||||
CollaborationModesConfig {
|
||||
default_mode_request_user_input: config
|
||||
.features
|
||||
.enabled(Feature::DefaultModeRequestUserInput),
|
||||
},
|
||||
Arc::new(EnvironmentManager::from_env()),
|
||||
);
|
||||
let thread = thread_manager.start_thread(config).await?;
|
||||
|
||||
let output = build_prompt_input_from_session(thread.thread.codex.session.as_ref(), input).await;
|
||||
let shutdown = thread.thread.shutdown_and_wait().await;
|
||||
let _removed = thread_manager.remove_thread(&thread.thread_id).await;
|
||||
|
||||
shutdown?;
|
||||
output
|
||||
}
|
||||
|
||||
pub(crate) async fn build_prompt_input_from_session(
|
||||
sess: &Session,
|
||||
input: Vec<UserInput>,
|
||||
) -> CodexResult<Vec<ResponseItem>> {
|
||||
let turn_context = sess.new_default_turn().await;
|
||||
sess.record_context_updates_and_set_reference_context_item(turn_context.as_ref())
|
||||
.await;
|
||||
|
||||
if !input.is_empty() {
|
||||
let input_item = ResponseInputItem::from(input);
|
||||
let response_item = ResponseItem::from(input_item);
|
||||
sess.record_conversation_items(turn_context.as_ref(), std::slice::from_ref(&response_item))
|
||||
.await;
|
||||
}
|
||||
|
||||
let prompt_input = sess
|
||||
.clone_history()
|
||||
.await
|
||||
.for_prompt(&turn_context.model_info.input_modalities);
|
||||
let router = built_tools(
|
||||
sess,
|
||||
turn_context.as_ref(),
|
||||
&prompt_input,
|
||||
&HashSet::new(),
|
||||
Some(turn_context.turn_skills.outcome.as_ref()),
|
||||
&CancellationToken::new(),
|
||||
)
|
||||
.await?;
|
||||
let base_instructions = sess.get_base_instructions().await;
|
||||
let prompt = build_prompt(
|
||||
prompt_input,
|
||||
router.as_ref(),
|
||||
turn_context.as_ref(),
|
||||
base_instructions,
|
||||
);
|
||||
|
||||
Ok(prompt.get_formatted_input())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::config::test_config;
|
||||
|
||||
use super::build_prompt_input;
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_prompt_input_includes_context_and_user_message() {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
let cwd = tempfile::tempdir().expect("create cwd");
|
||||
let mut config = test_config();
|
||||
config.codex_home = codex_home.path().to_path_buf();
|
||||
config.cwd = AbsolutePathBuf::try_from(cwd.path().to_path_buf()).expect("absolute cwd");
|
||||
config.user_instructions = Some("Project-specific test instructions".to_string());
|
||||
|
||||
let input = build_prompt_input(
|
||||
config,
|
||||
vec![UserInput::Text {
|
||||
text: "hello from debug prompt".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
)
|
||||
.await
|
||||
.expect("build prompt input");
|
||||
|
||||
let expected_user_message = ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: "hello from debug prompt".to_string(),
|
||||
}],
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
assert_eq!(input.last(), Some(&expected_user_message));
|
||||
assert!(input.iter().any(|item| {
|
||||
let ResponseItem::Message { content, .. } = item else {
|
||||
return false;
|
||||
};
|
||||
|
||||
content.iter().any(|content_item| {
|
||||
let (ContentItem::InputText { text } | ContentItem::OutputText { text }) =
|
||||
content_item
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
text.contains("Project-specific test instructions")
|
||||
})
|
||||
}));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user