Skip to content

Commit 8e49a2c

Browse files
authored
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`: <img width="477" height="157" alt="Screenshot 2026-01-09 at 15 45 34" src="https://github.com/user-attachments/assets/630ce68f-c856-4a2b-a004-7df2fbe5de93" />
1 parent af1ed26 commit 8e49a2c

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

codex-rs/tui/src/status/card.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use ratatui::prelude::*;
1717
use ratatui::style::Stylize;
1818
use std::collections::BTreeSet;
1919
use std::path::PathBuf;
20+
use url::Url;
2021

2122
use super::account::StatusAccountDisplay;
2223
use super::format::FieldFormatter;
@@ -62,6 +63,7 @@ struct StatusHistoryCell {
6263
approval: String,
6364
sandbox: String,
6465
agents_summary: String,
66+
model_provider: Option<String>,
6567
account: Option<StatusAccountDisplay>,
6668
session_id: Option<String>,
6769
token_usage: StatusTokenUsageData,
@@ -129,6 +131,7 @@ impl StatusHistoryCell {
129131
}
130132
};
131133
let agents_summary = compose_agents_summary(config);
134+
let model_provider = format_model_provider(config);
132135
let account = compose_account_display(auth_manager, plan_type);
133136
let session_id = session_id.as_ref().map(std::string::ToString::to_string);
134137
let default_usage = TokenUsage::default();
@@ -157,6 +160,7 @@ impl StatusHistoryCell {
157160
approval,
158161
sandbox,
159162
agents_summary,
163+
model_provider,
160164
account,
161165
session_id,
162166
token_usage,
@@ -338,6 +342,9 @@ impl HistoryCell for StatusHistoryCell {
338342
.collect();
339343
let mut seen: BTreeSet<String> = labels.iter().cloned().collect();
340344

345+
if self.model_provider.is_some() {
346+
push_label(&mut labels, &mut seen, "Model provider");
347+
}
341348
if account_value.is_some() {
342349
push_label(&mut labels, &mut seen, "Account");
343350
}
@@ -381,6 +388,9 @@ impl HistoryCell for StatusHistoryCell {
381388
let directory_value = format_directory_display(&self.directory, Some(value_width));
382389

383390
lines.push(formatter.line("Model", model_spans));
391+
if let Some(model_provider) = self.model_provider.as_ref() {
392+
lines.push(formatter.line("Model provider", vec![Span::from(model_provider.clone())]));
393+
}
384394
lines.push(formatter.line("Directory", vec![Span::from(directory_value)]));
385395
lines.push(formatter.line("Approval", vec![Span::from(self.approval.clone())]));
386396
lines.push(formatter.line("Sandbox", vec![Span::from(self.sandbox.clone())]));
@@ -416,3 +426,39 @@ impl HistoryCell for StatusHistoryCell {
416426
with_border_with_inner_width(truncated_lines, inner_width)
417427
}
418428
}
429+
430+
fn format_model_provider(config: &Config) -> Option<String> {
431+
let provider = &config.model_provider;
432+
let name = provider.name.trim();
433+
let provider_name = if name.is_empty() {
434+
config.model_provider_id.as_str()
435+
} else {
436+
name
437+
};
438+
let base_url = provider.base_url.as_deref().and_then(sanitize_base_url);
439+
let is_default_openai = provider.is_openai() && base_url.is_none();
440+
if is_default_openai {
441+
return None;
442+
}
443+
444+
Some(match base_url {
445+
Some(base_url) => format!("{provider_name} - {base_url}"),
446+
None => provider_name.to_string(),
447+
})
448+
}
449+
450+
fn sanitize_base_url(raw: &str) -> Option<String> {
451+
let trimmed = raw.trim();
452+
if trimmed.is_empty() {
453+
return None;
454+
}
455+
456+
let Ok(mut url) = Url::parse(trimmed) else {
457+
return None;
458+
};
459+
let _ = url.set_username("");
460+
let _ = url.set_password(None);
461+
url.set_query(None);
462+
url.set_fragment(None);
463+
Some(url.to_string().trim_end_matches('/').to_string()).filter(|value| !value.is_empty())
464+
}

codex-rs/tui2/src/status/card.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use ratatui::prelude::*;
1717
use ratatui::style::Stylize;
1818
use std::collections::BTreeSet;
1919
use std::path::PathBuf;
20+
use url::Url;
2021

2122
use super::account::StatusAccountDisplay;
2223
use super::format::FieldFormatter;
@@ -62,6 +63,7 @@ struct StatusHistoryCell {
6263
approval: String,
6364
sandbox: String,
6465
agents_summary: String,
66+
model_provider: Option<String>,
6567
account: Option<StatusAccountDisplay>,
6668
session_id: Option<String>,
6769
token_usage: StatusTokenUsageData,
@@ -129,6 +131,7 @@ impl StatusHistoryCell {
129131
}
130132
};
131133
let agents_summary = compose_agents_summary(config);
134+
let model_provider = format_model_provider(config);
132135
let account = compose_account_display(auth_manager, plan_type);
133136
let session_id = session_id.as_ref().map(std::string::ToString::to_string);
134137
let default_usage = TokenUsage::default();
@@ -157,6 +160,7 @@ impl StatusHistoryCell {
157160
approval,
158161
sandbox,
159162
agents_summary,
163+
model_provider,
160164
account,
161165
session_id,
162166
token_usage,
@@ -338,6 +342,9 @@ impl HistoryCell for StatusHistoryCell {
338342
.collect();
339343
let mut seen: BTreeSet<String> = labels.iter().cloned().collect();
340344

345+
if self.model_provider.is_some() {
346+
push_label(&mut labels, &mut seen, "Model provider");
347+
}
341348
if account_value.is_some() {
342349
push_label(&mut labels, &mut seen, "Account");
343350
}
@@ -380,6 +387,9 @@ impl HistoryCell for StatusHistoryCell {
380387
let directory_value = format_directory_display(&self.directory, Some(value_width));
381388

382389
lines.push(formatter.line("Model", model_spans));
390+
if let Some(model_provider) = self.model_provider.as_ref() {
391+
lines.push(formatter.line("Model provider", vec![Span::from(model_provider.clone())]));
392+
}
383393
lines.push(formatter.line("Directory", vec![Span::from(directory_value)]));
384394
lines.push(formatter.line("Approval", vec![Span::from(self.approval.clone())]));
385395
lines.push(formatter.line("Sandbox", vec![Span::from(self.sandbox.clone())]));
@@ -415,3 +425,39 @@ impl HistoryCell for StatusHistoryCell {
415425
with_border_with_inner_width(truncated_lines, inner_width)
416426
}
417427
}
428+
429+
fn format_model_provider(config: &Config) -> Option<String> {
430+
let provider = &config.model_provider;
431+
let name = provider.name.trim();
432+
let provider_name = if name.is_empty() {
433+
config.model_provider_id.as_str()
434+
} else {
435+
name
436+
};
437+
let base_url = provider.base_url.as_deref().and_then(sanitize_base_url);
438+
let is_default_openai = provider.is_openai() && base_url.is_none();
439+
if is_default_openai {
440+
return None;
441+
}
442+
443+
Some(match base_url {
444+
Some(base_url) => format!("{provider_name} - {base_url}"),
445+
None => provider_name.to_string(),
446+
})
447+
}
448+
449+
fn sanitize_base_url(raw: &str) -> Option<String> {
450+
let trimmed = raw.trim();
451+
if trimmed.is_empty() {
452+
return None;
453+
}
454+
455+
let Ok(mut url) = Url::parse(trimmed) else {
456+
return None;
457+
};
458+
let _ = url.set_username("");
459+
let _ = url.set_password(None);
460+
url.set_query(None);
461+
url.set_fragment(None);
462+
Some(url.to_string().trim_end_matches('/').to_string()).filter(|value| !value.is_empty())
463+
}

0 commit comments

Comments
 (0)