mirror of
https://github.com/openai/codex.git
synced 2026-09-17 12:23:33 +00:00
feat(core): allow guardian prompt overrides from model metadata
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -29,6 +29,7 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo {
|
||||
priority,
|
||||
upgrade: preset.upgrade.as_ref().map(|u| u.into()),
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -77,6 +77,7 @@ async fn models_client_hits_models_endpoint() {
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -538,11 +538,19 @@ async fn run_guardian_subagent(
|
||||
};
|
||||
(turn.model_info.slug.clone(), reasoning_effort)
|
||||
};
|
||||
let guardian_model_info = session
|
||||
.services
|
||||
.models_manager
|
||||
.get_model_info(&guardian_model, turn.config.as_ref())
|
||||
.await;
|
||||
let guardian_config = build_guardian_subagent_config(
|
||||
turn.config.as_ref(),
|
||||
live_network_config,
|
||||
guardian_model.as_str(),
|
||||
guardian_reasoning_effort,
|
||||
guardian_model_info
|
||||
.guardian_developer_instructions
|
||||
.as_deref(),
|
||||
)?;
|
||||
|
||||
// Reuse the standard interactive subagent runner so we can seed inherited
|
||||
@@ -608,11 +616,12 @@ fn build_guardian_subagent_config(
|
||||
live_network_config: Option<codex_network_proxy::NetworkProxyConfig>,
|
||||
active_model: &str,
|
||||
reasoning_effort: Option<codex_protocol::openai_models::ReasoningEffort>,
|
||||
guardian_prompt_override: Option<&str>,
|
||||
) -> anyhow::Result<Config> {
|
||||
let mut guardian_config = parent_config.clone();
|
||||
guardian_config.model = Some(active_model.to_string());
|
||||
guardian_config.model_reasoning_effort = reasoning_effort;
|
||||
guardian_config.developer_instructions = Some(guardian_policy_prompt());
|
||||
guardian_config.developer_instructions = Some(guardian_policy_prompt(guardian_prompt_override));
|
||||
guardian_config.permissions.approval_policy = Constrained::allow_only(AskForApproval::Never);
|
||||
guardian_config.permissions.sandbox_policy =
|
||||
Constrained::allow_only(SandboxPolicy::new_read_only_policy());
|
||||
@@ -818,8 +827,10 @@ fn guardian_output_contract_prompt() -> &'static str {
|
||||
/// Keep the prompt in a dedicated markdown file so reviewers can audit prompt
|
||||
/// changes directly without diffing through code. The output contract is
|
||||
/// appended from code so it stays near `guardian_output_schema()`.
|
||||
fn guardian_policy_prompt() -> String {
|
||||
let prompt = include_str!("guardian_prompt.md").trim_end();
|
||||
fn guardian_policy_prompt(prompt_override: Option<&str>) -> String {
|
||||
let prompt = prompt_override
|
||||
.unwrap_or(include_str!("guardian_prompt.md"))
|
||||
.trim_end();
|
||||
format!("{prompt}\n\n{}\n", guardian_output_contract_prompt())
|
||||
}
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ fn guardian_subagent_config_preserves_parent_network_proxy() {
|
||||
None,
|
||||
"parent-active-model",
|
||||
Some(codex_protocol::openai_models::ReasoningEffort::Low),
|
||||
None,
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
@@ -278,6 +279,7 @@ fn guardian_subagent_config_uses_live_network_proxy_state() {
|
||||
Some(live_network.clone()),
|
||||
"active-model",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
@@ -308,7 +310,7 @@ fn guardian_subagent_config_rejects_pinned_collab_feature() {
|
||||
)
|
||||
.expect("managed features");
|
||||
|
||||
let err = build_guardian_subagent_config(&parent_config, None, "active-model", None)
|
||||
let err = build_guardian_subagent_config(&parent_config, None, "active-model", None, None)
|
||||
.expect_err("guardian config should fail when collab is pinned on");
|
||||
|
||||
assert!(
|
||||
@@ -323,8 +325,27 @@ fn guardian_subagent_config_uses_parent_active_model_instead_of_hardcoded_slug()
|
||||
parent_config.model = Some("configured-model".to_string());
|
||||
|
||||
let guardian_config =
|
||||
build_guardian_subagent_config(&parent_config, None, "active-model", None)
|
||||
build_guardian_subagent_config(&parent_config, None, "active-model", None, None)
|
||||
.expect("guardian config");
|
||||
|
||||
assert_eq!(guardian_config.model, Some("active-model".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn guardian_subagent_config_prefers_model_prompt_override() {
|
||||
let guardian_config = build_guardian_subagent_config(
|
||||
&test_config(),
|
||||
None,
|
||||
"active-model",
|
||||
None,
|
||||
Some("override prompt"),
|
||||
)
|
||||
.expect("guardian config");
|
||||
|
||||
let instructions = guardian_config
|
||||
.developer_instructions
|
||||
.expect("guardian instructions");
|
||||
|
||||
assert!(instructions.starts_with("override prompt"));
|
||||
assert!(instructions.contains("\"risk_level\": \"low\" | \"medium\" | \"high\""));
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ pub(crate) fn model_info_from_slug(slug: &str) -> ModelInfo {
|
||||
availability_nux: None,
|
||||
upgrade: None,
|
||||
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: local_personality_messages_for_slug(slug),
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -58,6 +58,7 @@ fn test_model_info(
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
@@ -678,6 +679,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result<
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -335,6 +335,7 @@ fn test_remote_model(slug: &str, priority: i32) -> ModelInfo {
|
||||
priority,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -633,6 +633,7 @@ async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: Some(ModelMessages {
|
||||
instructions_template: Some("Base instructions\n{{ personality }}\n".to_string()),
|
||||
instructions_variables: Some(ModelInstructionsVariables {
|
||||
@@ -748,6 +749,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: Some(ModelMessages {
|
||||
instructions_template: Some("Base instructions\n{{ personality }}\n".to_string()),
|
||||
instructions_variables: Some(ModelInstructionsVariables {
|
||||
|
||||
@@ -294,6 +294,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
@@ -536,6 +537,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: remote_base.to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
@@ -1002,6 +1004,7 @@ fn test_remote_model_with_policy(
|
||||
priority,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -400,6 +400,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -996,6 +996,7 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: None,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
@@ -246,6 +246,8 @@ pub struct ModelInfo {
|
||||
pub upgrade: Option<ModelInfoUpgrade>,
|
||||
pub base_instructions: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub guardian_developer_instructions: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub model_messages: Option<ModelMessages>,
|
||||
pub supports_reasoning_summaries: bool,
|
||||
#[serde(default)]
|
||||
@@ -521,6 +523,7 @@ mod tests {
|
||||
availability_nux: None,
|
||||
upgrade: None,
|
||||
base_instructions: "base".to_string(),
|
||||
guardian_developer_instructions: None,
|
||||
model_messages: spec,
|
||||
supports_reasoning_summaries: false,
|
||||
default_reasoning_summary: ReasoningSummary::Auto,
|
||||
|
||||
Reference in New Issue
Block a user