mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Source Guardian v2 defaults from the model catalog (#38990)
## What changed - Add optional `guardian_v2` model-message defaults for classifier instructions, review thresholds, reasoning effort, transcript selection and limits, and action and compaction token limits. - Apply those defaults when Guardian v2 samples a tool call, while preserving explicit `[features.guardianv2]` settings over catalog values. - Preserve the new catalog configuration when applying model overrides. ## Testing - Cover model-catalog serialization and model override behavior. - Verify that sampling uses catalog defaults while retaining local overrides. GitOrigin-RevId: 4ae9ec235c647acbca05a20b7c4b87a4e1331161
This commit is contained in:
@@ -33,6 +33,12 @@ use crate::config_types::ServiceTier;
|
||||
use crate::config_types::Verbosity;
|
||||
use crate::protocol::MultiAgentVersion;
|
||||
|
||||
#[path = "openai_models/guardian_v2.rs"]
|
||||
mod guardian_v2;
|
||||
|
||||
pub use guardian_v2::GuardianV2ModelConfig;
|
||||
pub use guardian_v2::GuardianV2TranscriptModelConfig;
|
||||
|
||||
const PERSONALITY_PLACEHOLDER: &str = "{{ personality }}";
|
||||
/// Backend model-catalog specialty identifying cybersecurity-focused models.
|
||||
pub const MODEL_SPECIALTY_CYBER: &str = "cyber";
|
||||
@@ -534,6 +540,8 @@ pub struct ModelMessages {
|
||||
pub multi_agent: Option<MultiAgentMessages>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub token_budget: Option<ModelTokenBudgetConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub guardian_v2: Option<GuardianV2ModelConfig>,
|
||||
}
|
||||
|
||||
/// Model-owned defaults for the context-window token-budget feature.
|
||||
@@ -758,6 +766,7 @@ where
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
});
|
||||
messages.instructions_template = Some(base_instructions);
|
||||
}
|
||||
@@ -947,6 +956,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1088,6 +1098,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1169,6 +1180,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
}));
|
||||
|
||||
let instructions = model.get_model_instructions(Some(Personality::Friendly));
|
||||
@@ -1191,6 +1203,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
}));
|
||||
assert_eq!(
|
||||
model.get_model_instructions(Some(Personality::Pragmatic)),
|
||||
@@ -1214,6 +1227,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
}));
|
||||
assert_eq!(
|
||||
model_no_personality.get_model_instructions(Some(Personality::Friendly)),
|
||||
@@ -1248,6 +1262,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
}));
|
||||
|
||||
let instructions = model.get_model_instructions(Some(Personality::Friendly));
|
||||
@@ -1288,6 +1303,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -1339,6 +1355,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
}))],
|
||||
};
|
||||
|
||||
@@ -1376,6 +1393,17 @@ mod tests {
|
||||
}),
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: Some(GuardianV2ModelConfig {
|
||||
classifier_instructions: Some("Guardian classification".to_string()),
|
||||
review_threshold_basis_points: Some(7_500),
|
||||
reasoning_effort: Some(ReasoningEffort::Minimal),
|
||||
transcript: Some(GuardianV2TranscriptModelConfig {
|
||||
sources: Some(vec!["reasoning".to_string()]),
|
||||
max_tool_entry_tokens: Some(500),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
};
|
||||
let mut value = serde_json::to_value(ModelsResponse {
|
||||
models: vec![test_model(Some(messages.clone()))],
|
||||
@@ -1398,6 +1426,7 @@ mod tests {
|
||||
permissions: None,
|
||||
multi_agent: None,
|
||||
token_budget: None,
|
||||
guardian_v2: None,
|
||||
};
|
||||
let mut value = serde_json::to_value(ModelsResponse {
|
||||
models: vec![test_model(Some(canonical_messages.clone()))],
|
||||
|
||||
42
codex-rs/protocol/src/openai_models/guardian_v2.rs
Normal file
42
codex-rs/protocol/src/openai_models/guardian_v2.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
use super::ReasoningEffort;
|
||||
|
||||
/// Optional model-owned defaults for Guardian v2 classification experiments.
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, TS, JsonSchema)]
|
||||
pub struct GuardianV2ModelConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub classifier_instructions: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub review_threshold_basis_points: Option<u16>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning_effort: Option<ReasoningEffort>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub transcript: Option<GuardianV2TranscriptModelConfig>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_action_tokens: Option<usize>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_classifier_instruction_tokens: Option<usize>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_parent_compaction_tokens: Option<usize>,
|
||||
}
|
||||
|
||||
/// Optional model-owned defaults for selecting and bounding Guardian v2 history.
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, TS, JsonSchema)]
|
||||
pub struct GuardianV2TranscriptModelConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub sources: Option<Vec<String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_message_entry_tokens: Option<usize>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_tool_entry_tokens: Option<usize>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_message_transcript_tokens: Option<usize>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_tool_transcript_tokens: Option<usize>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub max_recent_non_user_entries: Option<usize>,
|
||||
}
|
||||
Reference in New Issue
Block a user