From f05fd0e6610b11ddd9bd9bbf211d056d9bb765de Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Mon, 25 May 2026 09:53:39 -0700 Subject: [PATCH] TUI config cleanup: oss_provider (#24254) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Manual provider selection during `codex --oss` startup was still persisting `oss_provider` through the legacy local `config.toml` writer. That bypasses the app-server-owned config mutation path used by the TUI, so this routes the write through the app server config API instead. The net behavior is intentionally narrow: only an interactive picker selection is persisted. Auto-detected single-running-provider startup and explicit `--local-provider` startup remain ephemeral, so merely having one backend running does not make that provider sticky for future runs. ## What Changed - Removed the TUI picker’s direct dependency on `set_default_oss_provider`. - Had `oss_selection` report whether the returned provider came from the interactive picker. - Carried only manually selected providers into startup persistence. - Wrote `oss_provider` via `config/batchWrite` once the app server session is available. - Logged a warning and continued startup if the app-server config write fails. ## Verification Manually smoke-tested the real `codex-tui` binary with a temporary `CODEX_HOME`, pseudo-terminal input, and a fake LM Studio HTTP server: - Interactive picker selection persisted `oss_provider = "lmstudio"`. - Non-picker `--local-provider lmstudio` startup did not persist `oss_provider`. --- codex-rs/tui/src/config_update.rs | 4 ++++ codex-rs/tui/src/lib.rs | 22 +++++++++++++++++++++- codex-rs/tui/src/oss_selection.rs | 31 ++++++++++++++++++------------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/codex-rs/tui/src/config_update.rs b/codex-rs/tui/src/config_update.rs index 9d73bb1a2..16ade0400 100644 --- a/codex-rs/tui/src/config_update.rs +++ b/codex-rs/tui/src/config_update.rs @@ -122,6 +122,10 @@ pub(crate) fn build_memory_settings_edits( ] } +pub(crate) fn build_oss_provider_edit(provider: &str) -> ConfigEdit { + replace_config_value("oss_provider", serde_json::json!(provider)) +} + pub(crate) async fn write_config_batch( request_handle: AppServerRequestHandle, edits: Vec, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 2683d3796..33b79b278 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -1002,6 +1002,7 @@ pub async fn run_main( ) .await; + let mut manually_selected_oss_provider = None; let model_provider_override = if cli.oss { let resolved = resolve_oss_provider(cli.oss_provider.as_deref(), &config_toml); @@ -1009,12 +1010,16 @@ pub async fn run_main( Some(provider) } else { // No provider configured, prompt the user - let provider = oss_selection::select_oss_provider(&codex_home).await?; + let selection = oss_selection::select_oss_provider().await?; + let provider = selection.provider; if provider == "__CANCELLED__" { return Err(std::io::Error::other( "OSS provider selection was cancelled by user", )); } + if selection.manually_selected { + manually_selected_oss_provider = Some(provider.clone()); + } Some(provider) } } else { @@ -1256,6 +1261,7 @@ pub async fn run_main( app_server_target, remote_cwd_override, config, + manually_selected_oss_provider, overrides, cli_kv_overrides, cloud_requirements, @@ -1277,6 +1283,7 @@ async fn run_ratatui_app( app_server_target: AppServerTarget, remote_cwd_override: Option, initial_config: Config, + manually_selected_oss_provider: Option, overrides: ConfigOverrides, cli_kv_overrides: Vec<(String, toml::Value)>, mut cloud_requirements: CloudRequirementsLoader, @@ -1356,6 +1363,19 @@ async fn run_ratatui_app( } } .with_remote_cwd_override(remote_cwd_override.clone()); + if let Some(provider) = manually_selected_oss_provider.as_deref() + && let Err(err) = config_update::write_config_batch( + app_server_session.request_handle(), + vec![config_update::build_oss_provider_edit(provider)], + ) + .await + { + warn!( + %err, + provider, + "Failed to persist selected OSS provider preference" + ); + } let mut app_server = Some(app_server_session); let should_show_trust_screen_flag = diff --git a/codex-rs/tui/src/oss_selection.rs b/codex-rs/tui/src/oss_selection.rs index ab9176ffb..cbb8e676f 100644 --- a/codex-rs/tui/src/oss_selection.rs +++ b/codex-rs/tui/src/oss_selection.rs @@ -4,7 +4,6 @@ use std::sync::LazyLock; use crate::key_hint; use crate::key_hint::KeyBinding; use crate::key_hint::KeyBindingListExt; -use crate::legacy_core::config::set_default_oss_provider; use codex_model_provider_info::DEFAULT_LMSTUDIO_PORT; use codex_model_provider_info::DEFAULT_OLLAMA_PORT; use codex_model_provider_info::LMSTUDIO_OSS_PROVIDER_ID; @@ -309,7 +308,12 @@ fn get_status_symbol_and_color(status: &ProviderStatus) -> (&'static str, Color) } } -pub async fn select_oss_provider(codex_home: &std::path::Path) -> io::Result { +pub(crate) struct OssProviderSelection { + pub(crate) provider: String, + pub(crate) manually_selected: bool, +} + +pub async fn select_oss_provider() -> io::Result { // Check provider statuses first let lmstudio_status = check_lmstudio_status().await; let ollama_status = check_ollama_status().await; @@ -318,11 +322,17 @@ pub async fn select_oss_provider(codex_home: &std::path::Path) -> io::Result { let provider = LMSTUDIO_OSS_PROVIDER_ID.to_string(); - return Ok(provider); + return Ok(OssProviderSelection { + provider, + manually_selected: false, + }); } (ProviderStatus::NotRunning, ProviderStatus::Running) => { let provider = OLLAMA_OSS_PROVIDER_ID.to_string(); - return Ok(provider); + return Ok(OssProviderSelection { + provider, + manually_selected: false, + }); } _ => { // Both running or both not running - show UI @@ -346,21 +356,16 @@ pub async fn select_oss_provider(codex_home: &std::path::Path) -> io::Result