mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Allow configuring the Guardian prompt template (#45516)
## What changed
Add `auto_review.experimental_policy_template` to override the Guardian prompt template in `config.toml`. Trim the configured value and ignore it when empty. Prefer the override over the model catalog template, retaining the bundled template as the final fallback.
The template's `{{ tenant_policy_config }}` placeholder is replaced with the resolved Guardian policy.
## Testing
Extend tests to cover template deserialization, trimming, precedence over the catalog template, and rendered policy text in Guardian inference requests.
GitOrigin-RevId: 85b4a8fc193a42735354894203ccbd1f738a3b58
This commit is contained in:
@@ -553,6 +553,8 @@ pub enum ThreadStoreToml {
|
||||
pub struct AutoReviewToml {
|
||||
/// Additional policy instructions inserted into the guardian prompt.
|
||||
pub policy: Option<String>,
|
||||
/// Experimental full Guardian prompt template containing the tenant policy placeholder.
|
||||
pub experimental_policy_template: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
|
||||
|
||||
@@ -383,6 +383,10 @@
|
||||
},
|
||||
"AutoReviewToml": {
|
||||
"properties": {
|
||||
"experimental_policy_template": {
|
||||
"description": "Experimental full Guardian prompt template containing the tenant policy placeholder.",
|
||||
"type": "string"
|
||||
},
|
||||
"policy": {
|
||||
"description": "Additional policy instructions inserted into the guardian prompt.",
|
||||
"type": "string"
|
||||
@@ -7208,4 +7212,4 @@
|
||||
},
|
||||
"title": "ConfigToml",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8232,29 +8232,38 @@ async fn load_config_uses_requirements_guardian_policy_config() -> std::io::Resu
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_toml_deserializes_auto_review_policy() {
|
||||
fn config_toml_deserializes_auto_review_policy_and_template() {
|
||||
let cfg = toml::from_str::<ConfigToml>(
|
||||
r#"
|
||||
[auto_review]
|
||||
policy = "Use the user-configured guardian policy."
|
||||
experimental_policy_template = "Configured template: {{ tenant_policy_config }}"
|
||||
"#,
|
||||
)
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
let auto_review = cfg.auto_review.as_ref().expect("auto-review config");
|
||||
assert_eq!(
|
||||
cfg.auto_review
|
||||
.as_ref()
|
||||
.and_then(|auto_review| auto_review.policy.as_deref()),
|
||||
Some("Use the user-configured guardian policy.")
|
||||
(
|
||||
auto_review.policy.as_deref(),
|
||||
auto_review.experimental_policy_template.as_deref(),
|
||||
),
|
||||
(
|
||||
Some("Use the user-configured guardian policy."),
|
||||
Some("Configured template: {{ tenant_policy_config }}"),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_config_uses_auto_review_guardian_policy_config() -> std::io::Result<()> {
|
||||
async fn load_config_uses_auto_review_guardian_policy_config_and_template() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let cfg = ConfigToml {
|
||||
auto_review: Some(AutoReviewToml {
|
||||
policy: Some(" Use the user-configured guardian policy. ".to_string()),
|
||||
experimental_policy_template: Some(
|
||||
" Configured template: {{ tenant_policy_config }} ".to_string(),
|
||||
),
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
@@ -8270,8 +8279,14 @@ async fn load_config_uses_auto_review_guardian_policy_config() -> std::io::Resul
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
config.guardian_policy_config.as_deref(),
|
||||
Some("Use the user-configured guardian policy.")
|
||||
(
|
||||
config.guardian_policy_config.as_deref(),
|
||||
config.guardian_policy_template.as_deref(),
|
||||
),
|
||||
(
|
||||
Some("Use the user-configured guardian policy."),
|
||||
Some("Configured template: {{ tenant_policy_config }}"),
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -8292,6 +8307,7 @@ async fn requirements_guardian_policy_beats_auto_review() -> std::io::Result<()>
|
||||
let cfg = ConfigToml {
|
||||
auto_review: Some(AutoReviewToml {
|
||||
policy: Some("Use the user-configured guardian policy.".to_string()),
|
||||
experimental_policy_template: None,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
@@ -8322,6 +8338,7 @@ async fn load_config_ignores_empty_auto_review_guardian_policy_config() -> std::
|
||||
let cfg = ConfigToml {
|
||||
auto_review: Some(AutoReviewToml {
|
||||
policy: Some(" ".to_string()),
|
||||
experimental_policy_template: None,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -683,6 +683,11 @@ pub struct Config {
|
||||
/// guardian developer prompt.
|
||||
pub guardian_policy_config: Option<String>,
|
||||
|
||||
/// Guardian prompt template override from config.toml.
|
||||
/// The resolved policy config replaces its `{{ tenant_policy_config }}`
|
||||
/// placeholder when a review session is built.
|
||||
pub guardian_policy_template: Option<String>,
|
||||
|
||||
/// Whether to inject the `<permissions instructions>` developer block.
|
||||
pub include_permissions_instructions: bool,
|
||||
|
||||
@@ -3910,6 +3915,14 @@ impl Config {
|
||||
auto_review.policy.as_deref(),
|
||||
))
|
||||
});
|
||||
let guardian_policy_template = cfg
|
||||
.auto_review
|
||||
.as_ref()
|
||||
.and_then(|auto_review| {
|
||||
normalize_guardian_policy_config(
|
||||
auto_review.experimental_policy_template.as_deref(),
|
||||
)
|
||||
});
|
||||
let personality = personality.or(cfg.personality);
|
||||
|
||||
let experimental_compact_prompt_path = cfg.experimental_compact_prompt_file.as_ref();
|
||||
@@ -4253,6 +4266,7 @@ impl Config {
|
||||
.or(show_raw_agent_reasoning)
|
||||
.unwrap_or(false),
|
||||
guardian_policy_config,
|
||||
guardian_policy_template,
|
||||
model_reasoning_effort: cfg.model_reasoning_effort,
|
||||
plan_mode_reasoning_effort: cfg.plan_mode_reasoning_effort,
|
||||
model_reasoning_summary: cfg.model_reasoning_summary,
|
||||
|
||||
@@ -626,11 +626,13 @@ async fn guardian_review_session_config_disables_skill_instructions() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn guardian_review_session_config_prefers_managed_policy_and_uses_catalog_template() {
|
||||
async fn guardian_review_session_config_prefers_configured_policy_and_template() {
|
||||
let mut parent_config = crate::config::test_config().await;
|
||||
let managed_policy = "Use the managed Guardian policy.";
|
||||
let configured_template = "Configured Guardian template:\n{{ tenant_policy_config }}";
|
||||
let catalog_template = "Catalog Guardian template:\n{{ tenant_policy_config }}";
|
||||
parent_config.guardian_policy_config = Some(managed_policy.to_string());
|
||||
parent_config.guardian_policy_template = Some(configured_template.to_string());
|
||||
let model_messages = ModelMessages {
|
||||
persistent_instructions: None,
|
||||
tools: None,
|
||||
@@ -667,7 +669,7 @@ async fn guardian_review_session_config_prefers_managed_policy_and_uses_catalog_
|
||||
guardian_config.base_instructions,
|
||||
Some(guardian_policy_prompt_with_config_and_template(
|
||||
managed_policy,
|
||||
catalog_template,
|
||||
configured_template,
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,8 +47,10 @@ pub fn build_guardian_review_session_config(
|
||||
}
|
||||
let catalog_auto_review = model_messages.and_then(|messages| messages.auto_review.as_ref());
|
||||
let tenant_policy_config = parent_config.resolve_guardian_policy(model_messages);
|
||||
let policy_template = catalog_auto_review
|
||||
.and_then(|messages| messages.policy_template.as_deref())
|
||||
let policy_template = parent_config
|
||||
.guardian_policy_template
|
||||
.as_deref()
|
||||
.or_else(|| catalog_auto_review.and_then(|messages| messages.policy_template.as_deref()))
|
||||
.unwrap_or(BUNDLED_GUARDIAN_POLICY_TEMPLATE);
|
||||
guardian_config.base_instructions = Some(guardian_policy_prompt_with_config_and_template(
|
||||
tenant_policy_config,
|
||||
|
||||
@@ -153,6 +153,8 @@ async fn guardian_session_inherits_parent_http_fallback(
|
||||
) -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let configured_policy = "Use the task-configured Guardian policy.";
|
||||
let configured_template = "You are judging one planned coding-agent action.\nConfigured template: {{ tenant_policy_config }}";
|
||||
let server = start_mock_server().await;
|
||||
let websocket_fallback = Mock::given(method("GET"))
|
||||
.and(path_regex(".*/responses$"))
|
||||
@@ -205,6 +207,8 @@ async fn guardian_session_inherits_parent_http_fallback(
|
||||
config.model_provider.stream_max_retries = Some(1);
|
||||
config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
|
||||
config.approvals_reviewer = ApprovalsReviewer::User;
|
||||
config.guardian_policy_config = Some(configured_policy.to_string());
|
||||
config.guardian_policy_template = Some(configured_template.to_string());
|
||||
});
|
||||
let test = builder.build_with_auto_env(&server).await?;
|
||||
|
||||
@@ -247,6 +251,10 @@ async fn guardian_session_inherits_parent_http_fallback(
|
||||
request.body_json()["client_metadata"]["x-openai-subagent"].as_str() == Some("guardian")
|
||||
})
|
||||
.expect("Guardian reviewer inference request");
|
||||
assert!(
|
||||
guardian_request
|
||||
.body_contains_text("Configured template: Use the task-configured Guardian policy.")
|
||||
);
|
||||
assert_eq!(guardian_request.path(), expected_guardian_path);
|
||||
let credits_enabled = expected_guardian_path.ends_with("/guardian");
|
||||
let body = guardian_request.body_json();
|
||||
|
||||
@@ -213,6 +213,7 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
|
||||
base_instructions_provenance: None,
|
||||
developer_instructions: None,
|
||||
guardian_policy_config: None,
|
||||
guardian_policy_template: None,
|
||||
include_permissions_instructions: false,
|
||||
include_apps_instructions: false,
|
||||
include_collaboration_mode_instructions: false,
|
||||
|
||||
Reference in New Issue
Block a user