From f07b8aa591f3afbcb09f2abbe58f008794b46255 Mon Sep 17 00:00:00 2001 From: gt-oai Date: Wed, 7 Jan 2026 21:24:18 +0000 Subject: [PATCH] Warn in /model if BASE_URL set (#8847) Screenshot 2026-01-07 at 18 37 59 It may not make sense to use the `/model` menu with a custom OPENAI_BASE_URL. But some model proxies may support it, so we shouldn't disable it completely. A warning is a reasonable compromise. --- codex-rs/tui/src/chatwidget.rs | 57 +++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 868319893..537adda25 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -147,6 +147,7 @@ use strum::IntoEnumIterator; const USER_SHELL_COMMAND_HELP_TITLE: &str = "Prefix a command with ! to run it locally"; const USER_SHELL_COMMAND_HELP_HINT: &str = "Example: !ls"; +const DEFAULT_OPENAI_BASE_URL: &str = "https://api.openai.com/v1"; // Track information about an in-flight exec command. struct RunningCommand { command: Vec, @@ -2453,6 +2454,45 @@ impl ChatWidget { self.open_model_popup_with_presets(presets); } + fn model_menu_header(&self, title: &str, subtitle: &str) -> Box { + let title = title.to_string(); + let subtitle = subtitle.to_string(); + let mut header = ColumnRenderable::new(); + header.push(Line::from(title.bold())); + header.push(Line::from(subtitle.dim())); + if let Some(warning) = self.model_menu_warning_line() { + header.push(warning); + } + Box::new(header) + } + + fn model_menu_warning_line(&self) -> Option> { + let base_url = self.custom_openai_base_url()?; + let warning = format!( + "Warning: OPENAI_BASE_URL is set to {base_url}. Selecting models may not be supported or work properly." + ); + Some(Line::from(warning.red())) + } + + fn custom_openai_base_url(&self) -> Option { + if !self.config.model_provider.is_openai() { + return None; + } + + let base_url = self.config.model_provider.base_url.as_ref()?; + let trimmed = base_url.trim(); + if trimmed.is_empty() { + return None; + } + + let normalized = trimmed.trim_end_matches('/'); + if normalized == DEFAULT_OPENAI_BASE_URL { + return None; + } + + Some(trimmed.to_string()) + } + pub(crate) fn open_model_popup_with_presets(&mut self, presets: Vec) { let presets: Vec = presets .into_iter() @@ -2521,11 +2561,14 @@ impl ChatWidget { }); } + let header = self.model_menu_header( + "Select Model", + "Pick a quick auto mode or browse all models.", + ); self.bottom_pane.show_selection_view(SelectionViewParams { - title: Some("Select Model".to_string()), - subtitle: Some("Pick a quick auto mode or browse all models.".to_string()), footer_hint: Some(standard_popup_hint_line()), items, + header, ..Default::default() }); } @@ -2576,14 +2619,14 @@ impl ChatWidget { }); } + let header = self.model_menu_header( + "Select Model and Effort", + "Access legacy models by running codex -m or in your config.toml", + ); self.bottom_pane.show_selection_view(SelectionViewParams { - title: Some("Select Model and Effort".to_string()), - subtitle: Some( - "Access legacy models by running codex -m or in your config.toml" - .to_string(), - ), footer_hint: Some("Press enter to select reasoning effort, or esc to dismiss.".into()), items, + header, ..Default::default() }); }