mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Move default realtime prompt into core (#17165)
- Adds a core-owned realtime backend prompt template and preparation path. - Makes omitted realtime start prompts use the core default, while null or empty prompts intentionally send empty instructions. - Covers the core realtime path and app-server v2 path with integration coverage. --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
36586eafed
commit
4c2a1ae31b
@@ -13,6 +13,7 @@ mod client_common;
|
||||
pub(crate) mod codex;
|
||||
mod realtime_context;
|
||||
mod realtime_conversation;
|
||||
mod realtime_prompt;
|
||||
pub use codex::SteerInputError;
|
||||
mod codex_thread;
|
||||
mod compact_remote;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::client::ModelClient;
|
||||
use crate::codex::Session;
|
||||
use crate::realtime_context::build_realtime_startup_context;
|
||||
use crate::realtime_prompt::prepare_realtime_backend_prompt;
|
||||
use async_channel::Receiver;
|
||||
use async_channel::Sender;
|
||||
use async_channel::TrySendError;
|
||||
@@ -546,14 +547,14 @@ async fn prepare_realtime_start(
|
||||
|
||||
pub(crate) async fn build_realtime_session_config(
|
||||
sess: &Arc<Session>,
|
||||
prompt: String,
|
||||
prompt: Option<Option<String>>,
|
||||
session_id: Option<String>,
|
||||
) -> CodexResult<RealtimeSessionConfig> {
|
||||
let config = sess.get_config().await;
|
||||
let prompt = config
|
||||
.experimental_realtime_ws_backend_prompt
|
||||
.clone()
|
||||
.unwrap_or(prompt);
|
||||
let prompt = prepare_realtime_backend_prompt(
|
||||
prompt,
|
||||
config.experimental_realtime_ws_backend_prompt.clone(),
|
||||
);
|
||||
let startup_context = match config.experimental_realtime_ws_startup_context.clone() {
|
||||
Some(startup_context) => startup_context,
|
||||
None => {
|
||||
@@ -562,10 +563,11 @@ pub(crate) async fn build_realtime_session_config(
|
||||
.unwrap_or_default()
|
||||
}
|
||||
};
|
||||
let prompt = if startup_context.is_empty() {
|
||||
prompt
|
||||
} else {
|
||||
format!("{prompt}\n\n{startup_context}")
|
||||
let prompt = match (prompt.is_empty(), startup_context.is_empty()) {
|
||||
(true, true) => String::new(),
|
||||
(true, false) => startup_context,
|
||||
(false, true) => prompt,
|
||||
(false, false) => format!("{prompt}\n\n{startup_context}"),
|
||||
};
|
||||
let model = config.experimental_realtime_ws_model.clone();
|
||||
let event_parser = match config.realtime.version {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
const BACKEND_PROMPT: &str = include_str!("../templates/realtime/backend_prompt.md");
|
||||
const DEFAULT_USER_FIRST_NAME: &str = "there";
|
||||
const USER_FIRST_NAME_PLACEHOLDER: &str = "{{ user_first_name }}";
|
||||
|
||||
pub(crate) fn prepare_realtime_backend_prompt(
|
||||
prompt: Option<Option<String>>,
|
||||
config_prompt: Option<String>,
|
||||
) -> String {
|
||||
if let Some(config_prompt) = config_prompt
|
||||
&& !config_prompt.trim().is_empty()
|
||||
{
|
||||
return config_prompt;
|
||||
}
|
||||
|
||||
match prompt {
|
||||
Some(Some(prompt)) => return prompt,
|
||||
Some(None) => return String::new(),
|
||||
None => {}
|
||||
}
|
||||
|
||||
BACKEND_PROMPT
|
||||
.trim_end()
|
||||
.replace(USER_FIRST_NAME_PLACEHOLDER, ¤t_user_first_name())
|
||||
}
|
||||
|
||||
fn current_user_first_name() -> String {
|
||||
[whoami::realname(), whoami::username()]
|
||||
.into_iter()
|
||||
.filter_map(|name| name.split_whitespace().next().map(str::to_string))
|
||||
.find(|name| !name.is_empty())
|
||||
.unwrap_or_else(|| DEFAULT_USER_FIRST_NAME.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::prepare_realtime_backend_prompt;
|
||||
|
||||
#[test]
|
||||
fn prepare_realtime_backend_prompt_prefers_config_override() {
|
||||
assert_eq!(
|
||||
prepare_realtime_backend_prompt(
|
||||
Some(Some("prompt from request".to_string())),
|
||||
Some("prompt from config".to_string()),
|
||||
),
|
||||
"prompt from config"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_realtime_backend_prompt_uses_request_prompt() {
|
||||
assert_eq!(
|
||||
prepare_realtime_backend_prompt(
|
||||
Some(Some("prompt from request".to_string())),
|
||||
/*config_prompt*/ None,
|
||||
),
|
||||
"prompt from request"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_realtime_backend_prompt_preserves_empty_request_prompt() {
|
||||
assert_eq!(
|
||||
prepare_realtime_backend_prompt(Some(Some(String::new())), /*config_prompt*/ None),
|
||||
""
|
||||
);
|
||||
assert_eq!(
|
||||
prepare_realtime_backend_prompt(Some(None), /*config_prompt*/ None),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepare_realtime_backend_prompt_renders_default() {
|
||||
let prompt =
|
||||
prepare_realtime_backend_prompt(/*prompt*/ None, /*config_prompt*/ None);
|
||||
|
||||
assert!(prompt.starts_with("You are **Codex**"));
|
||||
assert!(prompt.contains("The user's name is "));
|
||||
assert!(!prompt.contains("{{ user_first_name }}"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user