diff --git a/codex-rs/core/prompt.md b/codex-rs/core/prompt.md index 4e55003b..f194eba4 100644 --- a/codex-rs/core/prompt.md +++ b/codex-rs/core/prompt.md @@ -9,6 +9,8 @@ You MUST adhere to the following criteria when executing the task: - Analyzing code for vulnerabilities is allowed. - Showing user code and tool call details is allowed. - User instructions may overwrite the _CODING GUIDELINES_ section in this developer message. +- `user_instructions` are not part of the user's request, but guidance for how to complete the task. +- Do not cite `user_instructions` back to the user unless a specific piece is relevant. - Do not use \`ls -R\`, \`find\`, or \`grep\` - these are slow in large repos. Use \`rg\` and \`rg --files\`. - Use \`apply_patch\` to edit files: {"command":["apply_patch","*** Begin Patch\\n*** Update File: path/to/file.py\\n@@ def example():\\n- pass\\n+ return 123\\n*** End Patch"]} - If completing the user's task requires writing or modifying files: diff --git a/codex-rs/core/src/chat_completions.rs b/codex-rs/core/src/chat_completions.rs index b1dee853..b5ade23b 100644 --- a/codex-rs/core/src/chat_completions.rs +++ b/codex-rs/core/src/chat_completions.rs @@ -40,7 +40,7 @@ pub(crate) async fn stream_chat_completions( let full_instructions = prompt.get_full_instructions(model); messages.push(json!({"role": "system", "content": full_instructions})); - if let Some(instr) = &prompt.user_instructions { + if let Some(instr) = &prompt.get_formatted_user_instructions() { messages.push(json!({"role": "user", "content": instr})); } diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 1a8ae94f..00762a8a 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -144,11 +144,11 @@ impl ModelClient { }; let mut input_with_instructions = Vec::with_capacity(prompt.input.len() + 1); - if let Some(ui) = &prompt.user_instructions { + if let Some(ui) = prompt.get_formatted_user_instructions() { input_with_instructions.push(ResponseItem::Message { id: None, role: "user".to_string(), - content: vec![ContentItem::InputText { text: ui.clone() }], + content: vec![ContentItem::InputText { text: ui }], }); } input_with_instructions.extend(prompt.input.clone()); diff --git a/codex-rs/core/src/client_common.rs b/codex-rs/core/src/client_common.rs index 157f3587..6d9524cc 100644 --- a/codex-rs/core/src/client_common.rs +++ b/codex-rs/core/src/client_common.rs @@ -17,6 +17,10 @@ use tokio::sync::mpsc; /// with this content. const BASE_INSTRUCTIONS: &str = include_str!("../prompt.md"); +/// wraps user instructions message in a tag for the model to parse more easily. +const USER_INSTRUCTIONS_START: &str = "\n\n"; +const USER_INSTRUCTIONS_END: &str = "\n\n"; + /// API request payload for a single model turn. #[derive(Default, Debug, Clone)] pub struct Prompt { @@ -49,6 +53,12 @@ impl Prompt { } Cow::Owned(sections.join("\n")) } + + pub(crate) fn get_formatted_user_instructions(&self) -> Option { + self.user_instructions + .as_ref() + .map(|ui| format!("{USER_INSTRUCTIONS_START}{ui}{USER_INSTRUCTIONS_END}")) + } } #[derive(Debug)] diff --git a/codex-rs/core/tests/client.rs b/codex-rs/core/tests/client.rs index 06a110ea..f4930202 100644 --- a/codex-rs/core/tests/client.rs +++ b/codex-rs/core/tests/client.rs @@ -376,7 +376,13 @@ async fn includes_user_instructions_message_in_request() { request_body["input"][0]["content"][0]["text"] .as_str() .unwrap() - .starts_with("be nice") + .starts_with("\n\nbe nice") + ); + assert!( + request_body["input"][0]["content"][0]["text"] + .as_str() + .unwrap() + .ends_with("") ); }