feat: route guardian review model selection through providers (#22258)

## Why

Guardian review selection was hard-coded in `core`, which worked for the
default OpenAI path but did not give provider implementations a way to
choose backend-specific reviewer model IDs. That matters for Amazon
Bedrock: guardian review should run through the Bedrock/Mantle provider
using Bedrock's `openai.gpt-5.4` model ID, instead of accidentally
selecting a reviewer model that implies the OpenAI backend.

## What Changed

- Added provider-owned approval review model selection via
`ModelProvider::approval_review_model_selection`.
- Moved the existing default selection policy into the provider
abstraction: prefer the requested reviewer model when it is available,
otherwise fall back to the active turn model, preferring `Low` reasoning
when supported.
- Added an Amazon Bedrock override that pins guardian review to
`openai.gpt-5.4` with `Low` reasoning.
This commit is contained in:
Celia Chen
2026-05-13 01:55:46 +00:00
committed by GitHub
parent 51bfb5f3b1
commit e2eb7c30fe
7 changed files with 82 additions and 10 deletions
@@ -1,3 +1,4 @@
use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_4_MODEL_ID;
use codex_models_manager::model_info::BASE_INSTRUCTIONS;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::ServiceTier;
@@ -18,7 +19,6 @@ use codex_protocol::openai_models::WebSearchToolType;
const GPT_OSS_CONTEXT_WINDOW: i64 = 128_000;
const GPT_5_4_CONTEXT_WINDOW: i64 = 272_000;
const GPT_5_4_MAX_CONTEXT_WINDOW: i64 = 1_000_000;
const GPT_5_4_CMB_MODEL_ID: &str = "openai.gpt-5.4";
pub(crate) fn static_model_catalog() -> ModelsResponse {
ModelsResponse {
@@ -40,7 +40,7 @@ pub(crate) fn static_model_catalog() -> ModelsResponse {
fn gpt_5_4_cmb_bedrock_model(priority: i32) -> ModelInfo {
ModelInfo {
slug: GPT_5_4_CMB_MODEL_ID.to_string(),
slug: AMAZON_BEDROCK_GPT_5_4_MODEL_ID.to_string(),
display_name: "gpt-5.4".to_string(),
description: Some("Strong model for everyday coding.".to_string()),
default_reasoning_level: Some(ReasoningEffort::Medium),
@@ -155,7 +155,7 @@ mod tests {
let catalog = static_model_catalog();
assert_eq!(catalog.models.len(), 3);
assert_eq!(catalog.models[0].slug, GPT_5_4_CMB_MODEL_ID);
assert_eq!(catalog.models[0].slug, AMAZON_BEDROCK_GPT_5_4_MODEL_ID);
assert_eq!(catalog.models[1].slug, "openai.gpt-oss-120b");
assert_eq!(catalog.models[2].slug, "openai.gpt-oss-20b");
}
@@ -166,7 +166,7 @@ mod tests {
let cmb_model = catalog
.models
.iter()
.find(|model| model.slug == GPT_5_4_CMB_MODEL_ID)
.find(|model| model.slug == AMAZON_BEDROCK_GPT_5_4_MODEL_ID)
.expect("Bedrock catalog should include GPT-5.4 CMB");
assert_eq!(
@@ -9,6 +9,7 @@ use codex_api::Provider;
use codex_api::SharedAuthProvider;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_4_MODEL_ID;
use codex_model_provider_info::ModelProviderAwsAuthInfo;
use codex_model_provider_info::ModelProviderInfo;
use codex_models_manager::manager::SharedModelsManager;
@@ -62,6 +63,10 @@ impl ModelProvider for AmazonBedrockModelProvider {
}
}
fn approval_review_preferred_model(&self) -> &'static str {
AMAZON_BEDROCK_GPT_5_4_MODEL_ID
}
fn auth_manager(&self) -> Option<Arc<AuthManager>> {
None
}
@@ -140,4 +145,16 @@ mod tests {
}
);
}
#[test]
fn approval_review_preferred_model_uses_bedrock_gpt_5_4() {
let provider = AmazonBedrockModelProvider::new(
ModelProviderInfo::create_amazon_bedrock_provider(/*aws*/ None),
);
assert_eq!(
provider.approval_review_preferred_model(),
AMAZON_BEDROCK_GPT_5_4_MODEL_ID
);
}
}
+24
View File
@@ -70,6 +70,10 @@ impl std::error::Error for ProviderAccountError {}
pub type ProviderAccountResult = std::result::Result<ProviderAccountState, ProviderAccountError>;
/// Default model used for automatic approval review when a provider does not
/// require a backend-specific model ID.
pub const DEFAULT_APPROVAL_REVIEW_PREFERRED_MODEL: &str = "codex-auto-review";
/// Runtime provider abstraction used by model execution.
///
/// Implementations own provider-specific behavior for a model backend. The
@@ -85,6 +89,13 @@ pub trait ModelProvider: fmt::Debug + Send + Sync {
ProviderCapabilities::default()
}
/// Returns the preferred model used for automatic approval review.
///
/// Providers that require backend-specific model IDs should override this.
fn approval_review_preferred_model(&self) -> &'static str {
DEFAULT_APPROVAL_REVIEW_PREFERRED_MODEL
}
/// Returns whether requests made through this provider should include attestation.
fn supports_attestation(&self) -> bool {
false
@@ -350,6 +361,19 @@ mod tests {
assert_eq!(provider.capabilities(), ProviderCapabilities::default());
}
#[test]
fn configured_provider_uses_default_approval_review_preferred_model() {
let provider = create_model_provider(
ModelProviderInfo::create_openai_provider(/*base_url*/ None),
/*auth_manager*/ None,
);
assert_eq!(
provider.approval_review_preferred_model(),
DEFAULT_APPROVAL_REVIEW_PREFERRED_MODEL
);
}
#[tokio::test]
async fn configured_provider_runtime_base_url_uses_configured_base_url() {
let provider = create_model_provider(