mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
chore: rm remote models fflag (#11699)
rm `remote_models` feature flag. We see issues like #11527 when a user has `remote_models` disabled, as we always use the default fallback `ModelInfo`. This causes issues with model performance. Builds on #11690, which helps by warning the user when they are using the default fallback. This PR will make that happen much less frequently as an accidental consequence of disabling `remote_models`.
This commit is contained in:
committed by
GitHub
Unverified
parent
314029ffa3
commit
41800fc876
@@ -621,11 +621,10 @@ impl CodexMessageProcessor {
|
||||
ClientRequest::ModelList { request_id, params } => {
|
||||
let outgoing = self.outgoing.clone();
|
||||
let thread_manager = self.thread_manager.clone();
|
||||
let config = self.config.clone();
|
||||
let request_id = to_connection_request_id(request_id);
|
||||
|
||||
tokio::spawn(async move {
|
||||
Self::list_models(outgoing, thread_manager, config, request_id, params).await;
|
||||
Self::list_models(outgoing, thread_manager, request_id, params).await;
|
||||
});
|
||||
}
|
||||
ClientRequest::ExperimentalFeatureList { request_id, params } => {
|
||||
@@ -3621,7 +3620,6 @@ impl CodexMessageProcessor {
|
||||
async fn list_models(
|
||||
outgoing: Arc<OutgoingMessageSender>,
|
||||
thread_manager: Arc<ThreadManager>,
|
||||
config: Arc<Config>,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ModelListParams,
|
||||
) {
|
||||
@@ -3630,10 +3628,7 @@ impl CodexMessageProcessor {
|
||||
cursor,
|
||||
include_hidden,
|
||||
} = params;
|
||||
let mut config = (*config).clone();
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let models =
|
||||
supported_models(thread_manager, &config, include_hidden.unwrap_or(false)).await;
|
||||
let models = supported_models(thread_manager, include_hidden.unwrap_or(false)).await;
|
||||
let total = models.len();
|
||||
|
||||
if total == 0 {
|
||||
|
||||
@@ -3,18 +3,16 @@ use std::sync::Arc;
|
||||
use codex_app_server_protocol::Model;
|
||||
use codex_app_server_protocol::ReasoningEffortOption;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::models_manager::manager::RefreshStrategy;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ReasoningEffortPreset;
|
||||
|
||||
pub async fn supported_models(
|
||||
thread_manager: Arc<ThreadManager>,
|
||||
config: &Config,
|
||||
include_hidden: bool,
|
||||
) -> Vec<Model> {
|
||||
thread_manager
|
||||
.list_models(config, RefreshStrategy::OnlineIfUncached)
|
||||
.list_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter(|preset| include_hidden || preset.show_in_picker)
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn write_mock_responses_config_toml(
|
||||
compact_prompt: &str,
|
||||
) -> std::io::Result<()> {
|
||||
// Phase 1: build the features block for config.toml.
|
||||
let mut features = BTreeMap::from([(Feature::RemoteModels, false)]);
|
||||
let mut features = BTreeMap::new();
|
||||
for (feature, enabled) in feature_flags {
|
||||
features.insert(*feature, *enabled);
|
||||
}
|
||||
|
||||
@@ -215,10 +215,7 @@ async fn collect_turn_notifications(
|
||||
}
|
||||
|
||||
fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::Result<()> {
|
||||
let features = BTreeMap::from([
|
||||
(Feature::RemoteModels, false),
|
||||
(Feature::CollaborationModes, true),
|
||||
]);
|
||||
let features = BTreeMap::from([(Feature::CollaborationModes, true)]);
|
||||
let feature_entries = features
|
||||
.into_iter()
|
||||
.map(|(feature, enabled)| {
|
||||
|
||||
@@ -437,7 +437,6 @@ sandbox_mode = "read-only"
|
||||
model_provider = "mock_provider"
|
||||
|
||||
[features]
|
||||
remote_models = false
|
||||
shell_snapshot = false
|
||||
|
||||
[model_providers.mock_provider]
|
||||
|
||||
@@ -1008,7 +1008,6 @@ sandbox_mode = "read-only"
|
||||
model_provider = "mock_provider"
|
||||
|
||||
[features]
|
||||
remote_models = false
|
||||
personality = true
|
||||
|
||||
[model_providers.mock_provider]
|
||||
@@ -1038,7 +1037,6 @@ sandbox_mode = "read-only"
|
||||
model_provider = "mock_provider"
|
||||
|
||||
[features]
|
||||
remote_models = false
|
||||
personality = true
|
||||
|
||||
[model_providers.mock_provider]
|
||||
|
||||
@@ -1871,7 +1871,7 @@ fn create_config_toml_with_sandbox(
|
||||
feature_flags: &BTreeMap<Feature, bool>,
|
||||
sandbox_mode: &str,
|
||||
) -> std::io::Result<()> {
|
||||
let mut features = BTreeMap::from([(Feature::RemoteModels, false)]);
|
||||
let mut features = BTreeMap::new();
|
||||
for (feature, enabled) in feature_flags {
|
||||
features.insert(*feature, *enabled);
|
||||
}
|
||||
|
||||
@@ -329,15 +329,11 @@ impl Codex {
|
||||
|
||||
let config = Arc::new(config);
|
||||
let _ = models_manager
|
||||
.list_models(
|
||||
&config,
|
||||
crate::models_manager::manager::RefreshStrategy::OnlineIfUncached,
|
||||
)
|
||||
.list_models(crate::models_manager::manager::RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let model = models_manager
|
||||
.get_default_model(
|
||||
&config.model,
|
||||
&config,
|
||||
crate::models_manager::manager::RefreshStrategy::OnlineIfUncached,
|
||||
)
|
||||
.await;
|
||||
@@ -5583,11 +5579,7 @@ async fn try_run_sampling_request(
|
||||
}
|
||||
ResponseEvent::ModelsEtag(etag) => {
|
||||
// Update internal state with latest models etag
|
||||
let config = sess.get_config().await;
|
||||
sess.services
|
||||
.models_manager
|
||||
.refresh_if_new_etag(etag, &config)
|
||||
.await;
|
||||
sess.services.models_manager.refresh_if_new_etag(etag).await;
|
||||
}
|
||||
ResponseEvent::Completed {
|
||||
response_id: _,
|
||||
|
||||
@@ -777,7 +777,7 @@ unified_exec = true
|
||||
service
|
||||
.write_value(ConfigValueWriteParams {
|
||||
file_path: Some(tmp.path().join(CONFIG_TOML_FILE).display().to_string()),
|
||||
key_path: "features.remote_models".to_string(),
|
||||
key_path: "features.personality".to_string(),
|
||||
value: serde_json::json!(true),
|
||||
merge_strategy: MergeStrategy::Replace,
|
||||
expected_version: None,
|
||||
@@ -797,7 +797,7 @@ hide_full_access_warning = true
|
||||
|
||||
[features]
|
||||
unified_exec = true
|
||||
remote_models = true
|
||||
personality = true
|
||||
"#;
|
||||
assert_eq!(updated, expected);
|
||||
Ok(())
|
||||
|
||||
@@ -101,7 +101,7 @@ pub enum Feature {
|
||||
WindowsSandbox,
|
||||
/// Use the elevated Windows sandbox pipeline (setup + runner).
|
||||
WindowsSandboxElevated,
|
||||
/// Refresh remote models and emit AppReady once the list is available.
|
||||
/// Legacy remote models flag kept for backward compatibility.
|
||||
RemoteModels,
|
||||
/// Experimental shell snapshotting.
|
||||
ShellSnapshot,
|
||||
@@ -530,8 +530,8 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
FeatureSpec {
|
||||
id: Feature::RemoteModels,
|
||||
key: "remote_models",
|
||||
stage: Stage::Stable,
|
||||
default_enabled: true,
|
||||
stage: Stage::Removed,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::PowershellUtf8,
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::config::Config;
|
||||
use crate::default_client::build_reqwest_client;
|
||||
use crate::error::CodexErr;
|
||||
use crate::error::Result as CoreResult;
|
||||
use crate::features::Feature;
|
||||
use crate::model_provider_info::ModelProviderInfo;
|
||||
use crate::models_manager::collaboration_mode_presets::builtin_collaboration_mode_presets;
|
||||
use crate::models_manager::model_info;
|
||||
@@ -74,18 +73,11 @@ impl ModelsManager {
|
||||
/// List all available models, refreshing according to the specified strategy.
|
||||
///
|
||||
/// Returns model presets sorted by priority and filtered by auth mode and visibility.
|
||||
pub async fn list_models(
|
||||
&self,
|
||||
config: &Config,
|
||||
refresh_strategy: RefreshStrategy,
|
||||
) -> Vec<ModelPreset> {
|
||||
if let Err(err) = self
|
||||
.refresh_available_models(config, refresh_strategy)
|
||||
.await
|
||||
{
|
||||
pub async fn list_models(&self, refresh_strategy: RefreshStrategy) -> Vec<ModelPreset> {
|
||||
if let Err(err) = self.refresh_available_models(refresh_strategy).await {
|
||||
error!("failed to refresh available models: {err}");
|
||||
}
|
||||
let remote_models = self.get_remote_models(config).await;
|
||||
let remote_models = self.get_remote_models().await;
|
||||
self.build_available_models(remote_models)
|
||||
}
|
||||
|
||||
@@ -99,8 +91,8 @@ impl ModelsManager {
|
||||
/// Attempt to list models without blocking, using the current cached state.
|
||||
///
|
||||
/// Returns an error if the internal lock cannot be acquired.
|
||||
pub fn try_list_models(&self, config: &Config) -> Result<Vec<ModelPreset>, TryLockError> {
|
||||
let remote_models = self.try_get_remote_models(config)?;
|
||||
pub fn try_list_models(&self) -> Result<Vec<ModelPreset>, TryLockError> {
|
||||
let remote_models = self.try_get_remote_models()?;
|
||||
Ok(self.build_available_models(remote_models))
|
||||
}
|
||||
|
||||
@@ -112,19 +104,15 @@ impl ModelsManager {
|
||||
pub async fn get_default_model(
|
||||
&self,
|
||||
model: &Option<String>,
|
||||
config: &Config,
|
||||
refresh_strategy: RefreshStrategy,
|
||||
) -> String {
|
||||
if let Some(model) = model.as_ref() {
|
||||
return model.to_string();
|
||||
}
|
||||
if let Err(err) = self
|
||||
.refresh_available_models(config, refresh_strategy)
|
||||
.await
|
||||
{
|
||||
if let Err(err) = self.refresh_available_models(refresh_strategy).await {
|
||||
error!("failed to refresh available models: {err}");
|
||||
}
|
||||
let remote_models = self.get_remote_models(config).await;
|
||||
let remote_models = self.get_remote_models().await;
|
||||
let available = self.build_available_models(remote_models);
|
||||
available
|
||||
.iter()
|
||||
@@ -137,9 +125,7 @@ impl ModelsManager {
|
||||
// todo(aibrahim): look if we can tighten it to pub(crate)
|
||||
/// Look up model metadata, applying remote overrides and config adjustments.
|
||||
pub async fn get_model_info(&self, model: &str, config: &Config) -> ModelInfo {
|
||||
let remote = self
|
||||
.find_remote_model_by_longest_prefix(model, config)
|
||||
.await;
|
||||
let remote = self.find_remote_model_by_longest_prefix(model).await;
|
||||
let model_info = if let Some(remote) = remote {
|
||||
ModelInfo {
|
||||
slug: model.to_string(),
|
||||
@@ -152,13 +138,9 @@ impl ModelsManager {
|
||||
model_info::with_config_overrides(model_info, config)
|
||||
}
|
||||
|
||||
async fn find_remote_model_by_longest_prefix(
|
||||
&self,
|
||||
model: &str,
|
||||
config: &Config,
|
||||
) -> Option<ModelInfo> {
|
||||
async fn find_remote_model_by_longest_prefix(&self, model: &str) -> Option<ModelInfo> {
|
||||
let mut best: Option<ModelInfo> = None;
|
||||
for candidate in self.get_remote_models(config).await {
|
||||
for candidate in self.get_remote_models().await {
|
||||
if !model.starts_with(&candidate.slug) {
|
||||
continue;
|
||||
}
|
||||
@@ -177,7 +159,7 @@ impl ModelsManager {
|
||||
/// Refresh models if the provided ETag differs from the cached ETag.
|
||||
///
|
||||
/// Uses `Online` strategy to fetch latest models when ETags differ.
|
||||
pub(crate) async fn refresh_if_new_etag(&self, etag: String, config: &Config) {
|
||||
pub(crate) async fn refresh_if_new_etag(&self, etag: String) {
|
||||
let current_etag = self.get_etag().await;
|
||||
if current_etag.clone().is_some() && current_etag.as_deref() == Some(etag.as_str()) {
|
||||
if let Err(err) = self.cache_manager.renew_cache_ttl().await {
|
||||
@@ -185,23 +167,13 @@ impl ModelsManager {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if let Err(err) = self
|
||||
.refresh_available_models(config, RefreshStrategy::Online)
|
||||
.await
|
||||
{
|
||||
if let Err(err) = self.refresh_available_models(RefreshStrategy::Online).await {
|
||||
error!("failed to refresh available models: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Refresh available models according to the specified strategy.
|
||||
async fn refresh_available_models(
|
||||
&self,
|
||||
config: &Config,
|
||||
refresh_strategy: RefreshStrategy,
|
||||
) -> CoreResult<()> {
|
||||
if !config.features.enabled(Feature::RemoteModels) {
|
||||
return Ok(());
|
||||
}
|
||||
async fn refresh_available_models(&self, refresh_strategy: RefreshStrategy) -> CoreResult<()> {
|
||||
if self.auth_manager.auth_mode() != Some(AuthMode::Chatgpt) {
|
||||
if matches!(
|
||||
refresh_strategy,
|
||||
@@ -336,20 +308,12 @@ impl ModelsManager {
|
||||
merged_presets
|
||||
}
|
||||
|
||||
async fn get_remote_models(&self, config: &Config) -> Vec<ModelInfo> {
|
||||
if config.features.enabled(Feature::RemoteModels) {
|
||||
self.remote_models.read().await.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
async fn get_remote_models(&self) -> Vec<ModelInfo> {
|
||||
self.remote_models.read().await.clone()
|
||||
}
|
||||
|
||||
fn try_get_remote_models(&self, config: &Config) -> Result<Vec<ModelInfo>, TryLockError> {
|
||||
if config.features.enabled(Feature::RemoteModels) {
|
||||
Ok(self.remote_models.try_read()?.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
fn try_get_remote_models(&self) -> Result<Vec<ModelInfo>, TryLockError> {
|
||||
Ok(self.remote_models.try_read()?.clone())
|
||||
}
|
||||
|
||||
/// Construct a manager with a specific provider for testing.
|
||||
@@ -399,7 +363,6 @@ mod tests {
|
||||
use crate::CodexAuth;
|
||||
use crate::auth::AuthCredentialsStoreMode;
|
||||
use crate::config::ConfigBuilder;
|
||||
use crate::features::Feature;
|
||||
use crate::model_provider_info::WireApi;
|
||||
use chrono::Utc;
|
||||
use codex_protocol::openai_models::ModelsResponse;
|
||||
@@ -476,17 +439,16 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn get_model_info_tracks_fallback_usage() {
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let mut config = ConfigBuilder::default()
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::from_api_key("Test API Key"));
|
||||
let manager = ModelsManager::new(codex_home.path().to_path_buf(), auth_manager);
|
||||
let known_slug = manager
|
||||
.get_remote_models(&config)
|
||||
.get_remote_models()
|
||||
.await
|
||||
.first()
|
||||
.expect("bundled models should include at least one model")
|
||||
@@ -520,12 +482,6 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
let provider = provider_for(server.uri());
|
||||
@@ -536,15 +492,13 @@ mod tests {
|
||||
);
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("refresh succeeds");
|
||||
let cached_remote = manager.get_remote_models(&config).await;
|
||||
let cached_remote = manager.get_remote_models().await;
|
||||
assert_models_contain(&cached_remote, &remote_models);
|
||||
|
||||
let available = manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let available = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
let high_idx = available
|
||||
.iter()
|
||||
.position(|model| model.model == "priority-high")
|
||||
@@ -577,12 +531,6 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
let provider = provider_for(server.uri());
|
||||
@@ -593,17 +541,17 @@ mod tests {
|
||||
);
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("first refresh succeeds");
|
||||
assert_models_contain(&manager.get_remote_models(&config).await, &remote_models);
|
||||
assert_models_contain(&manager.get_remote_models().await, &remote_models);
|
||||
|
||||
// Second call should read from cache and avoid the network.
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("cached refresh succeeds");
|
||||
assert_models_contain(&manager.get_remote_models(&config).await, &remote_models);
|
||||
assert_models_contain(&manager.get_remote_models().await, &remote_models);
|
||||
assert_eq!(
|
||||
models_mock.requests().len(),
|
||||
1,
|
||||
@@ -624,12 +572,6 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
let provider = provider_for(server.uri());
|
||||
@@ -640,7 +582,7 @@ mod tests {
|
||||
);
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("initial refresh succeeds");
|
||||
|
||||
@@ -664,10 +606,10 @@ mod tests {
|
||||
.await;
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("second refresh succeeds");
|
||||
assert_models_contain(&manager.get_remote_models(&config).await, &updated_models);
|
||||
assert_models_contain(&manager.get_remote_models().await, &updated_models);
|
||||
assert_eq!(
|
||||
initial_mock.requests().len(),
|
||||
1,
|
||||
@@ -693,12 +635,6 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
let provider = provider_for(server.uri());
|
||||
@@ -709,7 +645,7 @@ mod tests {
|
||||
);
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("initial refresh succeeds");
|
||||
|
||||
@@ -733,10 +669,10 @@ mod tests {
|
||||
.await;
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("second refresh succeeds");
|
||||
assert_models_contain(&manager.get_remote_models(&config).await, &updated_models);
|
||||
assert_models_contain(&manager.get_remote_models().await, &updated_models);
|
||||
assert_eq!(
|
||||
initial_mock.requests().len(),
|
||||
1,
|
||||
@@ -762,12 +698,6 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let auth_manager =
|
||||
AuthManager::from_auth_for_testing(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
let provider = provider_for(server.uri());
|
||||
@@ -779,7 +709,7 @@ mod tests {
|
||||
manager.cache_manager.set_ttl(Duration::ZERO);
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("initial refresh succeeds");
|
||||
|
||||
@@ -794,12 +724,12 @@ mod tests {
|
||||
.await;
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.refresh_available_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await
|
||||
.expect("second refresh succeeds");
|
||||
|
||||
let available = manager
|
||||
.try_list_models(&config)
|
||||
.try_list_models()
|
||||
.expect("models should be available");
|
||||
assert!(
|
||||
available.iter().any(|preset| preset.model == "remote-new"),
|
||||
@@ -834,12 +764,6 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let codex_home = tempdir().expect("temp dir");
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.build()
|
||||
.await
|
||||
.expect("load default test config");
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let auth_manager = Arc::new(AuthManager::new(
|
||||
codex_home.path().to_path_buf(),
|
||||
false,
|
||||
@@ -853,10 +777,10 @@ mod tests {
|
||||
);
|
||||
|
||||
manager
|
||||
.refresh_available_models(&config, RefreshStrategy::Online)
|
||||
.refresh_available_models(RefreshStrategy::Online)
|
||||
.await
|
||||
.expect("refresh should no-op without chatgpt auth");
|
||||
let cached_remote = manager.get_remote_models(&config).await;
|
||||
let cached_remote = manager.get_remote_models().await;
|
||||
assert!(
|
||||
!cached_remote
|
||||
.iter()
|
||||
|
||||
@@ -230,12 +230,11 @@ impl ThreadManager {
|
||||
|
||||
pub async fn list_models(
|
||||
&self,
|
||||
config: &Config,
|
||||
refresh_strategy: crate::models_manager::manager::RefreshStrategy,
|
||||
) -> Vec<ModelPreset> {
|
||||
self.state
|
||||
.models_manager
|
||||
.list_models(config, refresh_strategy)
|
||||
.list_models(refresh_strategy)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use codex_core::built_in_model_providers;
|
||||
use codex_core::compact::SUMMARIZATION_PROMPT;
|
||||
use codex_core::compact::SUMMARY_PREFIX;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::ItemCompletedEvent;
|
||||
@@ -1708,7 +1707,6 @@ async fn pre_sampling_compact_runs_on_switch_to_smaller_context_model() {
|
||||
.with_config(move |config| {
|
||||
config.model_provider = model_provider;
|
||||
set_test_compact_prompt(config);
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
});
|
||||
let test = builder.build(&server).await.expect("build test codex");
|
||||
|
||||
@@ -1831,7 +1829,6 @@ async fn pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model() {
|
||||
.with_config(move |config| {
|
||||
config.model_provider = model_provider;
|
||||
set_test_compact_prompt(config);
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
});
|
||||
let initial = initial_builder
|
||||
.build(&server)
|
||||
@@ -1885,7 +1882,6 @@ async fn pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model() {
|
||||
.with_config(move |config| {
|
||||
config.model_provider = model_provider;
|
||||
set_test_compact_prompt(config);
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
});
|
||||
let resumed = resumed_builder
|
||||
.resume(&server, home, rollout_path)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use codex_core::CodexAuth;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::models_manager::manager::ModelsManager;
|
||||
use codex_protocol::openai_models::TruncationPolicyConfig;
|
||||
use core_test_support::load_default_config_for_test;
|
||||
@@ -9,8 +8,7 @@ use tempfile::TempDir;
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn offline_model_info_without_tool_output_override() {
|
||||
let codex_home = TempDir::new().expect("create temp dir");
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let config = load_default_config_for_test(&codex_home).await;
|
||||
let auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
|
||||
);
|
||||
@@ -28,7 +26,6 @@ async fn offline_model_info_without_tool_output_override() {
|
||||
async fn offline_model_info_with_tool_output_override() {
|
||||
let codex_home = TempDir::new().expect("create temp dir");
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.tool_output_token_limit = Some(123);
|
||||
let auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
|
||||
|
||||
@@ -266,13 +266,12 @@ async fn model_change_from_image_to_text_strips_prior_image_content() -> Result<
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(move |config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some(image_model_slug.to_string());
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
let _ = models_manager
|
||||
.list_models(&test.config, RefreshStrategy::OnlineIfUncached)
|
||||
.list_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let image_url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII="
|
||||
.to_string();
|
||||
@@ -434,15 +433,12 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result<
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some(large_model_slug.to_string());
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
let available_models = models_manager
|
||||
.list_models(&test.config, RefreshStrategy::Online)
|
||||
.await;
|
||||
let available_models = models_manager.list_models(RefreshStrategy::Online).await;
|
||||
assert!(
|
||||
available_models
|
||||
.iter()
|
||||
|
||||
@@ -36,7 +36,6 @@ async fn collect_tool_identifiers_for_model(model: &str) -> Vec<String> {
|
||||
.with_model(model)
|
||||
// Keep tool expectations stable when the default web_search mode changes.
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config
|
||||
.web_search_mode
|
||||
.set(WebSearchMode::Cached)
|
||||
|
||||
@@ -6,7 +6,6 @@ use chrono::DateTime;
|
||||
use chrono::TimeZone;
|
||||
use chrono::Utc;
|
||||
use codex_core::CodexAuth;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::models_manager::manager::RefreshStrategy;
|
||||
use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::Op;
|
||||
@@ -57,7 +56,6 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> {
|
||||
|
||||
let mut builder = test_codex().with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing());
|
||||
builder = builder.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some("gpt-5".to_string());
|
||||
config.model_provider.request_max_retries = Some(0);
|
||||
config.model_provider.stream_max_retries = Some(1);
|
||||
@@ -70,7 +68,7 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> {
|
||||
// Populate cache via initial refresh.
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
let _ = models_manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.list_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
|
||||
let cache_path = config.codex_home.join(CACHE_FILE);
|
||||
@@ -123,7 +121,7 @@ async fn renews_cache_ttl_on_matching_models_etag() -> Result<()> {
|
||||
// Cached models remain usable offline.
|
||||
let offline_models = test
|
||||
.thread_manager
|
||||
.list_models(&config, RefreshStrategy::Offline)
|
||||
.list_models(RefreshStrategy::Offline)
|
||||
.await;
|
||||
assert!(
|
||||
offline_models
|
||||
@@ -160,14 +158,13 @@ async fn uses_cache_when_version_matches() -> Result<()> {
|
||||
write_cache_sync(&cache_path, &cache).expect("write cache");
|
||||
})
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model_provider.request_max_retries = Some(0);
|
||||
});
|
||||
|
||||
let test = builder.build(&server).await?;
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
let models = models_manager
|
||||
.list_models(&test.config, RefreshStrategy::OnlineIfUncached)
|
||||
.list_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
@@ -208,14 +205,13 @@ async fn refreshes_when_cache_version_missing() -> Result<()> {
|
||||
write_cache_sync(&cache_path, &cache).expect("write cache");
|
||||
})
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model_provider.request_max_retries = Some(0);
|
||||
});
|
||||
|
||||
let test = builder.build(&server).await?;
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
let models = models_manager
|
||||
.list_models(&test.config, RefreshStrategy::OnlineIfUncached)
|
||||
.list_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
@@ -257,14 +253,13 @@ async fn refreshes_when_cache_version_differs() -> Result<()> {
|
||||
write_cache_sync(&cache_path, &cache).expect("write cache");
|
||||
})
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model_provider.request_max_retries = Some(0);
|
||||
});
|
||||
|
||||
let test = builder.build(&server).await?;
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
let models = models_manager
|
||||
.list_models(&test.config, RefreshStrategy::OnlineIfUncached)
|
||||
.list_models(RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use codex_core::CodexAuth;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::protocol::EventMsg;
|
||||
use codex_core::protocol::Op;
|
||||
@@ -48,7 +47,6 @@ async fn refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch
|
||||
.with_auth(auth)
|
||||
.with_model("gpt-5")
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
// Keep this test deterministic: no request retries, and a small stream retry budget.
|
||||
config.model_provider.request_max_retries = Some(0);
|
||||
config.model_provider.stream_max_retries = Some(1);
|
||||
|
||||
@@ -81,7 +81,6 @@ async fn user_turn_personality_none_does_not_add_update_message() -> anyhow::Res
|
||||
let mut builder = test_codex()
|
||||
.with_model("gpt-5.2-codex")
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
@@ -127,7 +126,6 @@ async fn config_personality_some_sets_instructions_template() -> anyhow::Result<
|
||||
let mut builder = test_codex()
|
||||
.with_model("gpt-5.2-codex")
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
config.personality = Some(Personality::Friendly);
|
||||
});
|
||||
@@ -181,7 +179,6 @@ async fn config_personality_none_sends_no_personality() -> anyhow::Result<()> {
|
||||
let mut builder = test_codex()
|
||||
.with_model("gpt-5.2-codex")
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
config.personality = Some(Personality::None);
|
||||
});
|
||||
@@ -242,7 +239,6 @@ async fn default_personality_is_pragmatic_without_config_toml() -> anyhow::Resul
|
||||
let mut builder = test_codex()
|
||||
.with_model("gpt-5.2-codex")
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
@@ -290,7 +286,6 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()>
|
||||
let mut builder = test_codex()
|
||||
.with_model("exp-codex-personality")
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
@@ -386,7 +381,6 @@ async fn user_turn_personality_same_value_does_not_add_update_message() -> anyho
|
||||
let mut builder = test_codex()
|
||||
.with_model("exp-codex-personality")
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
config.personality = Some(Personality::Pragmatic);
|
||||
});
|
||||
@@ -494,7 +488,6 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()>
|
||||
let mut builder = test_codex()
|
||||
.with_model("exp-codex-personality")
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.disable(Feature::Personality);
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
@@ -570,123 +563,6 @@ async fn user_turn_personality_skips_if_feature_disabled() -> anyhow::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn ignores_remote_personality_if_remote_models_disabled() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = MockServer::builder()
|
||||
.body_print_limit(BodyPrintLimit::Limited(80_000))
|
||||
.start()
|
||||
.await;
|
||||
|
||||
let remote_slug = "gpt-5.2-codex";
|
||||
let remote_personality_message = "Friendly from remote template";
|
||||
let remote_model = ModelInfo {
|
||||
slug: remote_slug.to_string(),
|
||||
display_name: "Remote personality test".to_string(),
|
||||
description: Some("Remote model with personality template".to_string()),
|
||||
default_reasoning_level: Some(ReasoningEffort::Medium),
|
||||
supported_reasoning_levels: vec![ReasoningEffortPreset {
|
||||
effort: ReasoningEffort::Medium,
|
||||
description: ReasoningEffort::Medium.to_string(),
|
||||
}],
|
||||
shell_type: ConfigShellToolType::UnifiedExec,
|
||||
visibility: ModelVisibility::List,
|
||||
supported_in_api: true,
|
||||
priority: 1,
|
||||
upgrade: None,
|
||||
base_instructions: "base instructions".to_string(),
|
||||
model_messages: Some(ModelMessages {
|
||||
instructions_template: Some("Base instructions\n{{ personality }}\n".to_string()),
|
||||
instructions_variables: Some(ModelInstructionsVariables {
|
||||
personality_default: None,
|
||||
personality_friendly: Some(remote_personality_message.to_string()),
|
||||
personality_pragmatic: None,
|
||||
}),
|
||||
}),
|
||||
supports_reasoning_summaries: false,
|
||||
support_verbosity: false,
|
||||
default_verbosity: None,
|
||||
apply_patch_tool_type: None,
|
||||
truncation_policy: TruncationPolicyConfig::bytes(10_000),
|
||||
supports_parallel_tool_calls: false,
|
||||
context_window: Some(128_000),
|
||||
auto_compact_token_limit: None,
|
||||
effective_context_window_percent: 95,
|
||||
experimental_supported_tools: Vec::new(),
|
||||
input_modalities: default_input_modalities(),
|
||||
prefer_websockets: false,
|
||||
used_fallback_model_metadata: false,
|
||||
};
|
||||
|
||||
let _models_mock = mount_models_once(
|
||||
&server,
|
||||
ModelsResponse {
|
||||
models: vec![remote_model],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let resp_mock = mount_sse_once(&server, sse_completed("resp-1")).await;
|
||||
|
||||
let mut builder = test_codex()
|
||||
.with_auth(codex_core::CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.disable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
config.model = Some(remote_slug.to_string());
|
||||
config.personality = Some(Personality::Friendly);
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
wait_for_model_available(
|
||||
&test.thread_manager.get_models_manager(),
|
||||
remote_slug,
|
||||
&test.config,
|
||||
)
|
||||
.await;
|
||||
|
||||
test.codex
|
||||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: test.cwd_path().to_path_buf(),
|
||||
approval_policy: AskForApproval::Never,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
model: remote_slug.to_string(),
|
||||
effort: test.config.model_reasoning_effort,
|
||||
summary: ReasoningSummary::Auto,
|
||||
collaboration_mode: None,
|
||||
personality: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
wait_for_event(&test.codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
|
||||
let request = resp_mock.single_request();
|
||||
let instructions_text = request.instructions_text();
|
||||
|
||||
assert!(
|
||||
instructions_text.contains("You are Codex, a coding agent based on GPT-5. You and the user share the same workspace and collaborate to achieve the user's goals."),
|
||||
"expected instructions to use the template instructions, got: {instructions_text:?}"
|
||||
);
|
||||
assert!(
|
||||
instructions_text.contains(
|
||||
"You optimize for team morale and being a supportive teammate as much as code quality."
|
||||
),
|
||||
"expected instructions to include the local friendly personality template, got: {instructions_text:?}"
|
||||
);
|
||||
assert!(
|
||||
!instructions_text.contains("{{ personality }}"),
|
||||
"expected legacy personality placeholder to be replaced, got: {instructions_text:?}"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -750,19 +626,13 @@ async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow
|
||||
let mut builder = test_codex()
|
||||
.with_auth(codex_core::CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
config.model = Some(remote_slug.to_string());
|
||||
config.personality = Some(Personality::Friendly);
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
wait_for_model_available(
|
||||
&test.thread_manager.get_models_manager(),
|
||||
remote_slug,
|
||||
&test.config,
|
||||
)
|
||||
.await;
|
||||
wait_for_model_available(&test.thread_manager.get_models_manager(), remote_slug).await;
|
||||
|
||||
test.codex
|
||||
.submit(Op::UserTurn {
|
||||
@@ -867,18 +737,12 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
|
||||
let mut builder = test_codex()
|
||||
.with_auth(codex_core::CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.features.enable(Feature::Personality);
|
||||
config.model = Some("gpt-5.2-codex".to_string());
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
wait_for_model_available(
|
||||
&test.thread_manager.get_models_manager(),
|
||||
remote_slug,
|
||||
&test.config,
|
||||
)
|
||||
.await;
|
||||
wait_for_model_available(&test.thread_manager.get_models_manager(), remote_slug).await;
|
||||
|
||||
test.codex
|
||||
.submit(Op::UserTurn {
|
||||
@@ -957,16 +821,10 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_model_available(
|
||||
manager: &Arc<ModelsManager>,
|
||||
slug: &str,
|
||||
config: &codex_core::config::Config,
|
||||
) {
|
||||
async fn wait_for_model_available(manager: &Arc<ModelsManager>, slug: &str) {
|
||||
let deadline = Instant::now() + Duration::from_secs(2);
|
||||
loop {
|
||||
let models = manager
|
||||
.list_models(config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let models = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
if models.iter().any(|model| model.model == slug) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,7 +194,6 @@ async fn gpt_5_tools_without_apply_patch_append_apply_patch_instructions() -> an
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.user_instructions = Some("be consistent and helpful".to_string());
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.features.disable(Feature::ApplyPatchFreeform);
|
||||
config.features.enable(Feature::CollaborationModes);
|
||||
config.model = Some("gpt-5".to_string());
|
||||
|
||||
@@ -7,8 +7,6 @@ use anyhow::Result;
|
||||
use codex_core::CodexAuth;
|
||||
use codex_core::ModelProviderInfo;
|
||||
use codex_core::built_in_model_providers;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::models_manager::manager::ModelsManager;
|
||||
use codex_core::models_manager::manager::RefreshStrategy;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
@@ -92,8 +90,7 @@ async fn remote_models_get_model_info_uses_longest_matching_prefix() -> Result<(
|
||||
.await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
let config = load_default_config_for_test(&codex_home).await;
|
||||
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let provider = ModelProviderInfo {
|
||||
@@ -106,9 +103,7 @@ async fn remote_models_get_model_info_uses_longest_matching_prefix() -> Result<(
|
||||
provider,
|
||||
);
|
||||
|
||||
manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
|
||||
let model_info = manager.get_model_info("gpt-5.3-codex-test", &config).await;
|
||||
|
||||
@@ -163,7 +158,6 @@ async fn remote_models_long_model_slug_is_sent_with_high_reasoning() -> Result<(
|
||||
} = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some(requested_model.to_string());
|
||||
})
|
||||
.build(&server)
|
||||
@@ -253,7 +247,6 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some("gpt-5.1".to_string());
|
||||
});
|
||||
let TestCodex {
|
||||
@@ -265,8 +258,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
||||
} = builder.build(&server).await?;
|
||||
|
||||
let models_manager = thread_manager.get_models_manager();
|
||||
let available_model =
|
||||
wait_for_model_available(&models_manager, REMOTE_MODEL_SLUG, &config).await;
|
||||
let available_model = wait_for_model_available(&models_manager, REMOTE_MODEL_SLUG).await;
|
||||
|
||||
assert_eq!(available_model.model, REMOTE_MODEL_SLUG);
|
||||
|
||||
@@ -375,13 +367,12 @@ async fn remote_models_truncation_policy_without_override_preserves_remote() ->
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some("gpt-5.1".to_string());
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
wait_for_model_available(&models_manager, slug, &test.config).await;
|
||||
wait_for_model_available(&models_manager, slug).await;
|
||||
|
||||
let model_info = models_manager.get_model_info(slug, &test.config).await;
|
||||
assert_eq!(
|
||||
@@ -420,14 +411,13 @@ async fn remote_models_truncation_policy_with_tool_output_override() -> Result<(
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some("gpt-5.1".to_string());
|
||||
config.tool_output_token_limit = Some(50);
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
let models_manager = test.thread_manager.get_models_manager();
|
||||
wait_for_model_available(&models_manager, slug, &test.config).await;
|
||||
wait_for_model_available(&models_manager, slug).await;
|
||||
|
||||
let model_info = models_manager.get_model_info(slug, &test.config).await;
|
||||
assert_eq!(
|
||||
@@ -502,7 +492,6 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some("gpt-5.1".to_string());
|
||||
});
|
||||
let TestCodex {
|
||||
@@ -514,7 +503,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
|
||||
} = builder.build(&server).await?;
|
||||
|
||||
let models_manager = thread_manager.get_models_manager();
|
||||
wait_for_model_available(&models_manager, model, &config).await;
|
||||
wait_for_model_available(&models_manager, model).await;
|
||||
|
||||
codex
|
||||
.submit(Op::OverrideTurnContext {
|
||||
@@ -574,8 +563,6 @@ async fn remote_models_preserve_builtin_presets() -> Result<()> {
|
||||
.await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let provider = ModelProviderInfo {
|
||||
@@ -588,9 +575,7 @@ async fn remote_models_preserve_builtin_presets() -> Result<()> {
|
||||
provider,
|
||||
);
|
||||
|
||||
let available = manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let available = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
let remote = available
|
||||
.iter()
|
||||
.find(|model| model.model == "remote-alpha")
|
||||
@@ -639,8 +624,6 @@ async fn remote_models_merge_adds_new_high_priority_first() -> Result<()> {
|
||||
.await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let provider = ModelProviderInfo {
|
||||
@@ -653,9 +636,7 @@ async fn remote_models_merge_adds_new_high_priority_first() -> Result<()> {
|
||||
provider,
|
||||
);
|
||||
|
||||
let available = manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let available = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
assert_eq!(
|
||||
available.first().map(|model| model.model.as_str()),
|
||||
Some("remote-top")
|
||||
@@ -690,8 +671,6 @@ async fn remote_models_merge_replaces_overlapping_model() -> Result<()> {
|
||||
.await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let provider = ModelProviderInfo {
|
||||
@@ -704,9 +683,7 @@ async fn remote_models_merge_replaces_overlapping_model() -> Result<()> {
|
||||
provider,
|
||||
);
|
||||
|
||||
let available = manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let available = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
let overridden = available
|
||||
.iter()
|
||||
.find(|model| model.model == slug)
|
||||
@@ -738,8 +715,6 @@ async fn remote_models_merge_preserves_bundled_models_on_empty_response() -> Res
|
||||
let _models_mock = mount_models_once(&server, ModelsResponse { models: Vec::new() }).await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let provider = ModelProviderInfo {
|
||||
@@ -752,9 +727,7 @@ async fn remote_models_merge_preserves_bundled_models_on_empty_response() -> Res
|
||||
provider,
|
||||
);
|
||||
|
||||
let available = manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let available = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
let bundled_slug = bundled_model_slug();
|
||||
assert!(
|
||||
available.iter().any(|model| model.model == bundled_slug),
|
||||
@@ -783,8 +756,6 @@ async fn remote_models_request_times_out_after_5s() -> Result<()> {
|
||||
.await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let provider = ModelProviderInfo {
|
||||
@@ -800,7 +771,7 @@ async fn remote_models_request_times_out_after_5s() -> Result<()> {
|
||||
let start = Instant::now();
|
||||
let model = timeout(
|
||||
Duration::from_secs(7),
|
||||
manager.get_default_model(&None, &config, RefreshStrategy::OnlineIfUncached),
|
||||
manager.get_default_model(&None, RefreshStrategy::OnlineIfUncached),
|
||||
)
|
||||
.await;
|
||||
let elapsed = start.elapsed();
|
||||
@@ -850,8 +821,6 @@ async fn remote_models_hide_picker_only_models() -> Result<()> {
|
||||
.await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let provider = ModelProviderInfo {
|
||||
@@ -865,13 +834,11 @@ async fn remote_models_hide_picker_only_models() -> Result<()> {
|
||||
);
|
||||
|
||||
let selected = manager
|
||||
.get_default_model(&None, &config, RefreshStrategy::OnlineIfUncached)
|
||||
.get_default_model(&None, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
assert_eq!(selected, "gpt-5.2-codex");
|
||||
|
||||
let available = manager
|
||||
.list_models(&config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let available = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
let hidden = available
|
||||
.iter()
|
||||
.find(|model| model.model == "codex-auto-balanced")
|
||||
@@ -888,17 +855,11 @@ async fn remote_models_hide_picker_only_models() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_model_available(
|
||||
manager: &Arc<ModelsManager>,
|
||||
slug: &str,
|
||||
config: &Config,
|
||||
) -> ModelPreset {
|
||||
async fn wait_for_model_available(manager: &Arc<ModelsManager>, slug: &str) -> ModelPreset {
|
||||
let deadline = Instant::now() + Duration::from_secs(2);
|
||||
loop {
|
||||
if let Some(model) = {
|
||||
let guard = manager
|
||||
.list_models(config, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
let guard = manager.list_models(RefreshStrategy::OnlineIfUncached).await;
|
||||
guard.iter().find(|model| model.model == slug).cloned()
|
||||
} {
|
||||
return model;
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::time::UNIX_EPOCH;
|
||||
use codex_core::CodexAuth;
|
||||
use codex_core::config::types::McpServerConfig;
|
||||
use codex_core::config::types::McpServerTransportConfig;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::models_manager::manager::RefreshStrategy;
|
||||
|
||||
use codex_core::protocol::AskForApproval;
|
||||
@@ -441,8 +440,6 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re
|
||||
let fixture = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(move |config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
|
||||
let mut servers = config.mcp_servers.get().clone();
|
||||
servers.insert(
|
||||
server_name.to_string(),
|
||||
@@ -478,7 +475,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re
|
||||
fixture
|
||||
.thread_manager
|
||||
.get_models_manager()
|
||||
.list_models(&fixture.config, RefreshStrategy::Online)
|
||||
.list_models(RefreshStrategy::Online)
|
||||
.await;
|
||||
assert_eq!(models_mock.requests().len(), 1);
|
||||
|
||||
|
||||
@@ -698,7 +698,6 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an
|
||||
let TestCodex { codex, cwd, .. } = test_codex()
|
||||
.with_auth(CodexAuth::create_dummy_chatgpt_auth_for_testing())
|
||||
.with_config(|config| {
|
||||
config.features.enable(Feature::RemoteModels);
|
||||
config.model = Some(model_slug.to_string());
|
||||
})
|
||||
.build(&server)
|
||||
|
||||
@@ -374,7 +374,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
));
|
||||
let default_model = thread_manager
|
||||
.get_models_manager()
|
||||
.get_default_model(&config.model, &config, RefreshStrategy::OnlineIfUncached)
|
||||
.get_default_model(&config.model, RefreshStrategy::OnlineIfUncached)
|
||||
.await;
|
||||
|
||||
// Handle resume subcommand by resolving a rollout path and using explicit resume API.
|
||||
|
||||
@@ -507,7 +507,6 @@ request_max_retries = 0
|
||||
stream_max_retries = 0
|
||||
|
||||
[features]
|
||||
remote_models = false
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1029,11 +1029,11 @@ impl App {
|
||||
));
|
||||
let mut model = thread_manager
|
||||
.get_models_manager()
|
||||
.get_default_model(&config.model, &config, RefreshStrategy::Offline)
|
||||
.get_default_model(&config.model, RefreshStrategy::Offline)
|
||||
.await;
|
||||
let available_models = thread_manager
|
||||
.get_models_manager()
|
||||
.list_models(&config, RefreshStrategy::Offline)
|
||||
.list_models(RefreshStrategy::Offline)
|
||||
.await;
|
||||
let exit_info = handle_model_migration_prompt_if_needed(
|
||||
tui,
|
||||
|
||||
@@ -4699,7 +4699,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn lower_cost_preset(&self) -> Option<ModelPreset> {
|
||||
let models = self.models_manager.try_list_models(&self.config).ok()?;
|
||||
let models = self.models_manager.try_list_models().ok()?;
|
||||
models
|
||||
.iter()
|
||||
.find(|preset| preset.show_in_picker && preset.model == NUDGE_MODEL_SLUG)
|
||||
@@ -4816,7 +4816,7 @@ impl ChatWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
let presets: Vec<ModelPreset> = match self.models_manager.try_list_models(&self.config) {
|
||||
let presets: Vec<ModelPreset> = match self.models_manager.try_list_models() {
|
||||
Ok(models) => models,
|
||||
Err(_) => {
|
||||
self.add_info_message(
|
||||
@@ -6138,7 +6138,7 @@ impl ChatWidget {
|
||||
fn current_model_supports_personality(&self) -> bool {
|
||||
let model = self.current_model();
|
||||
self.models_manager
|
||||
.try_list_models(&self.config)
|
||||
.try_list_models()
|
||||
.ok()
|
||||
.and_then(|models| {
|
||||
models
|
||||
@@ -6156,7 +6156,7 @@ impl ChatWidget {
|
||||
fn current_model_supports_images(&self) -> bool {
|
||||
let model = self.current_model();
|
||||
self.models_manager
|
||||
.try_list_models(&self.config)
|
||||
.try_list_models()
|
||||
.ok()
|
||||
.and_then(|models| {
|
||||
models
|
||||
|
||||
@@ -2749,7 +2749,7 @@ fn active_blob(chat: &ChatWidget) -> String {
|
||||
fn get_available_model(chat: &ChatWidget, model: &str) -> ModelPreset {
|
||||
let models = chat
|
||||
.models_manager
|
||||
.try_list_models(&chat.config)
|
||||
.try_list_models()
|
||||
.expect("models lock available");
|
||||
models
|
||||
.iter()
|
||||
|
||||
Reference in New Issue
Block a user