diff --git a/codex-rs/model-provider/src/amazon_bedrock/catalog.rs b/codex-rs/model-provider/src/amazon_bedrock/catalog.rs index 6117a832bb..f7727363fc 100644 --- a/codex-rs/model-provider/src/amazon_bedrock/catalog.rs +++ b/codex-rs/model-provider/src/amazon_bedrock/catalog.rs @@ -79,6 +79,19 @@ pub(crate) fn static_model_catalog() -> ModelsResponse { }) } +pub(super) fn static_gov_model_catalog() -> ModelsResponse { + let mut catalog = static_model_catalog(); + catalog.models.retain(|model| { + matches!( + model.slug.as_str(), + AMAZON_BEDROCK_GPT_5_6_TERRA_MODEL_ID + | AMAZON_BEDROCK_GPT_5_6_LUNA_MODEL_ID + | AMAZON_BEDROCK_GPT_5_4_MODEL_ID + ) + }); + catalog +} + pub(crate) fn normalize_bedrock_catalog(mut catalog: ModelsResponse) -> ModelsResponse { for model in &mut catalog.models { // Amazon Bedrock currently only supports the implicit "default" tier for GPT models. diff --git a/codex-rs/model-provider/src/amazon_bedrock/mod.rs b/codex-rs/model-provider/src/amazon_bedrock/mod.rs index e0e630def1..84c1d05a25 100644 --- a/codex-rs/model-provider/src/amazon_bedrock/mod.rs +++ b/codex-rs/model-provider/src/amazon_bedrock/mod.rs @@ -43,6 +43,7 @@ use crate::shared_state::process_shared_state; use auth::resolve_provider_auth as resolve_bedrock_provider_auth; pub(crate) use auth_refresh::AwsAuthRecovery; use catalog::normalize_bedrock_catalog; +use catalog::static_gov_model_catalog; pub(crate) use catalog::static_model_catalog; pub(crate) use credential_export::AwsCredentialExport; use mantle::bedrock_mantle_runtime_base_url; @@ -244,7 +245,25 @@ impl AmazonBedrockModelProvider { fn default_model_catalog(&self) -> ModelsResponse { match self.endpoint { - BedrockEndpoint::Mantle => static_model_catalog(), + BedrockEndpoint::Mantle => { + let endpoint = self + .info + .base_url + .as_deref() + .and_then(|base_url| url::Url::parse(base_url).ok()); + let is_govcloud = + endpoint + .as_ref() + .and_then(url::Url::host_str) + .is_some_and(|host| { + host.starts_with("bedrock-mantle.us-gov-") && host.ends_with(".api.aws") + }); + if is_govcloud { + static_gov_model_catalog() + } else { + static_model_catalog() + } + } BedrockEndpoint::Runtime => static_runtime_model_catalog(), } } diff --git a/codex-rs/model-provider/src/provider.rs b/codex-rs/model-provider/src/provider.rs index 1730e2a75b..acb7de1a75 100644 --- a/codex-rs/model-provider/src/provider.rs +++ b/codex-rs/model-provider/src/provider.rs @@ -1348,10 +1348,11 @@ printf '%s\n' '{"AccessKeyId":"exported","SecretAccessKey":"secret"}' assert!(!configured_model.additional_speed_tiers.is_empty()); assert!(!configured_model.service_tiers.is_empty()); - let provider = create_model_provider( - ModelProviderInfo::create_amazon_bedrock_provider(/*aws*/ None), - /*auth_manager*/ None, - ); + let mut provider_info = + ModelProviderInfo::create_amazon_bedrock_provider(/*aws*/ None); + provider_info.base_url = + Some("https://bedrock-mantle.us-gov-west-1.api.aws/openai/v1".to_string()); + let provider = create_model_provider(provider_info, /*auth_manager*/ None); let manager = provider.models_manager( test_codex_home(), Some(ModelsResponse { diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_govcloud_models.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_govcloud_models.snap new file mode 100644 index 0000000000..49ac7903d2 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__bedrock_govcloud_models.snap @@ -0,0 +1,12 @@ +--- +source: tui/src/chatwidget/tests/bedrock_catalog_tests.rs +expression: "render_bottom_popup(&chat, 100)" +--- + Select Model and Effort + + +› 1. GPT-5.6 Terra (current) Older balanced model for straightforward work. + 2. GPT-5.6 Luna Older fast and efficient model. + 3. GPT-5.4 Strong model for everyday coding. + + enter select · esc back diff --git a/codex-rs/tui/src/chatwidget/tests/bedrock_catalog_tests.rs b/codex-rs/tui/src/chatwidget/tests/bedrock_catalog_tests.rs index 5bcf9261b8..46e4a00f30 100644 --- a/codex-rs/tui/src/chatwidget/tests/bedrock_catalog_tests.rs +++ b/codex-rs/tui/src/chatwidget/tests/bedrock_catalog_tests.rs @@ -1,4 +1,4 @@ -//! Renders the provider catalog and Astra reasoning choices without changing the default model. +//! Renders Bedrock provider catalogs and Astra reasoning choices. use super::*; use codex_http_client::HttpClientFactory; @@ -51,3 +51,31 @@ async fn bedrock_astra_model_and_reasoning_pickers() { ); } } + +#[tokio::test] +async fn bedrock_govcloud_model_pickers() { + for region in ["us-gov-west-1", "us-gov-east-1"] { + let mut provider_info = + ModelProviderInfo::create_amazon_bedrock_provider(/*aws*/ None); + provider_info.base_url = Some(format!("https://bedrock-mantle.{region}.api.aws/openai/v1")); + let presets = create_model_provider(provider_info, /*auth_manager*/ None) + .models_manager_without_cache(/*config_model_catalog*/ None) + .list_models( + RefreshStrategy::Offline, + HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault), + ) + .await; + let default_preset = presets + .iter() + .find(|preset| preset.is_default) + .expect("default Bedrock model"); + let (mut chat, _events, _ops) = make_chatwidget_manual(Some(&default_preset.model)).await; + chat.thread_id = Some(ThreadId::new()); + chat.model_catalog = Arc::new(ModelCatalog::new(presets)); + chat.open_model_popup(); + assert_chatwidget_snapshot!( + "bedrock_govcloud_models", + render_bottom_popup(&chat, /*width*/ 100) + ); + } +}