From 8e49a2c0d108d8105838c95d862649604e6d4523 Mon Sep 17 00:00:00 2001 From: gt-oai Date: Sat, 10 Jan 2026 00:53:34 +0000 Subject: [PATCH] Add model provider info to /status if non-default (#8981) Add model provider info to /status if non-default Enterprises are running Codex and migrating between proxied / API key auth and SIWC. If you accidentally run Codex with `OPENAI_BASE_URL=...`, which is surprisingly easy to do, we don't tend to surface this anywhere and it may lead to breakage. One suggestion was to include this information in `/status`: Screenshot 2026-01-09 at 15 45 34 --- codex-rs/tui/src/status/card.rs | 46 ++++++++++++++++++++++++++++++++ codex-rs/tui2/src/status/card.rs | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/codex-rs/tui/src/status/card.rs b/codex-rs/tui/src/status/card.rs index 7bf066eeb..7174cf41c 100644 --- a/codex-rs/tui/src/status/card.rs +++ b/codex-rs/tui/src/status/card.rs @@ -17,6 +17,7 @@ use ratatui::prelude::*; use ratatui::style::Stylize; use std::collections::BTreeSet; use std::path::PathBuf; +use url::Url; use super::account::StatusAccountDisplay; use super::format::FieldFormatter; @@ -62,6 +63,7 @@ struct StatusHistoryCell { approval: String, sandbox: String, agents_summary: String, + model_provider: Option, account: Option, session_id: Option, token_usage: StatusTokenUsageData, @@ -129,6 +131,7 @@ impl StatusHistoryCell { } }; let agents_summary = compose_agents_summary(config); + let model_provider = format_model_provider(config); let account = compose_account_display(auth_manager, plan_type); let session_id = session_id.as_ref().map(std::string::ToString::to_string); let default_usage = TokenUsage::default(); @@ -157,6 +160,7 @@ impl StatusHistoryCell { approval, sandbox, agents_summary, + model_provider, account, session_id, token_usage, @@ -338,6 +342,9 @@ impl HistoryCell for StatusHistoryCell { .collect(); let mut seen: BTreeSet = labels.iter().cloned().collect(); + if self.model_provider.is_some() { + push_label(&mut labels, &mut seen, "Model provider"); + } if account_value.is_some() { push_label(&mut labels, &mut seen, "Account"); } @@ -381,6 +388,9 @@ impl HistoryCell for StatusHistoryCell { let directory_value = format_directory_display(&self.directory, Some(value_width)); lines.push(formatter.line("Model", model_spans)); + if let Some(model_provider) = self.model_provider.as_ref() { + lines.push(formatter.line("Model provider", vec![Span::from(model_provider.clone())])); + } lines.push(formatter.line("Directory", vec![Span::from(directory_value)])); lines.push(formatter.line("Approval", vec![Span::from(self.approval.clone())])); lines.push(formatter.line("Sandbox", vec![Span::from(self.sandbox.clone())])); @@ -416,3 +426,39 @@ impl HistoryCell for StatusHistoryCell { with_border_with_inner_width(truncated_lines, inner_width) } } + +fn format_model_provider(config: &Config) -> Option { + let provider = &config.model_provider; + let name = provider.name.trim(); + let provider_name = if name.is_empty() { + config.model_provider_id.as_str() + } else { + name + }; + let base_url = provider.base_url.as_deref().and_then(sanitize_base_url); + let is_default_openai = provider.is_openai() && base_url.is_none(); + if is_default_openai { + return None; + } + + Some(match base_url { + Some(base_url) => format!("{provider_name} - {base_url}"), + None => provider_name.to_string(), + }) +} + +fn sanitize_base_url(raw: &str) -> Option { + let trimmed = raw.trim(); + if trimmed.is_empty() { + return None; + } + + let Ok(mut url) = Url::parse(trimmed) else { + return None; + }; + let _ = url.set_username(""); + let _ = url.set_password(None); + url.set_query(None); + url.set_fragment(None); + Some(url.to_string().trim_end_matches('/').to_string()).filter(|value| !value.is_empty()) +} diff --git a/codex-rs/tui2/src/status/card.rs b/codex-rs/tui2/src/status/card.rs index 1cffb7efe..55896365e 100644 --- a/codex-rs/tui2/src/status/card.rs +++ b/codex-rs/tui2/src/status/card.rs @@ -17,6 +17,7 @@ use ratatui::prelude::*; use ratatui::style::Stylize; use std::collections::BTreeSet; use std::path::PathBuf; +use url::Url; use super::account::StatusAccountDisplay; use super::format::FieldFormatter; @@ -62,6 +63,7 @@ struct StatusHistoryCell { approval: String, sandbox: String, agents_summary: String, + model_provider: Option, account: Option, session_id: Option, token_usage: StatusTokenUsageData, @@ -129,6 +131,7 @@ impl StatusHistoryCell { } }; let agents_summary = compose_agents_summary(config); + let model_provider = format_model_provider(config); let account = compose_account_display(auth_manager, plan_type); let session_id = session_id.as_ref().map(std::string::ToString::to_string); let default_usage = TokenUsage::default(); @@ -157,6 +160,7 @@ impl StatusHistoryCell { approval, sandbox, agents_summary, + model_provider, account, session_id, token_usage, @@ -338,6 +342,9 @@ impl HistoryCell for StatusHistoryCell { .collect(); let mut seen: BTreeSet = labels.iter().cloned().collect(); + if self.model_provider.is_some() { + push_label(&mut labels, &mut seen, "Model provider"); + } if account_value.is_some() { push_label(&mut labels, &mut seen, "Account"); } @@ -380,6 +387,9 @@ impl HistoryCell for StatusHistoryCell { let directory_value = format_directory_display(&self.directory, Some(value_width)); lines.push(formatter.line("Model", model_spans)); + if let Some(model_provider) = self.model_provider.as_ref() { + lines.push(formatter.line("Model provider", vec![Span::from(model_provider.clone())])); + } lines.push(formatter.line("Directory", vec![Span::from(directory_value)])); lines.push(formatter.line("Approval", vec![Span::from(self.approval.clone())])); lines.push(formatter.line("Sandbox", vec![Span::from(self.sandbox.clone())])); @@ -415,3 +425,39 @@ impl HistoryCell for StatusHistoryCell { with_border_with_inner_width(truncated_lines, inner_width) } } + +fn format_model_provider(config: &Config) -> Option { + let provider = &config.model_provider; + let name = provider.name.trim(); + let provider_name = if name.is_empty() { + config.model_provider_id.as_str() + } else { + name + }; + let base_url = provider.base_url.as_deref().and_then(sanitize_base_url); + let is_default_openai = provider.is_openai() && base_url.is_none(); + if is_default_openai { + return None; + } + + Some(match base_url { + Some(base_url) => format!("{provider_name} - {base_url}"), + None => provider_name.to_string(), + }) +} + +fn sanitize_base_url(raw: &str) -> Option { + let trimmed = raw.trim(); + if trimmed.is_empty() { + return None; + } + + let Ok(mut url) = Url::parse(trimmed) else { + return None; + }; + let _ = url.set_username(""); + let _ = url.set_password(None); + url.set_query(None); + url.set_fragment(None); + Some(url.to_string().trim_end_matches('/').to_string()).filter(|value| !value.is_empty()) +}