mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
Add profile and provider status line items (#57516)
## What changed Add selectable `profile` and `provider` items to the TUI status line, including setup previews and theme colors. Render them as `profile: <name>` and `provider: <id>`, omitting the profile when unset and the provider until a thread is active. Accept `model-provider` as an alias for `provider`. ## Testing Extend status line tests to cover omitted values, the active thread's provider, provider updates, and a selected profile. Update the setup popup snapshot to include both items. GitOrigin-RevId: e7cbb7fb1e7017cfbd6ee71e1df7d0ddb1836ed4
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
//!
|
||||
//! # Available Status Line Items
|
||||
//!
|
||||
//! - Model information (name, reasoning level)
|
||||
//! - Model information (name, reasoning level, provider)
|
||||
//! - Named configuration profile
|
||||
//! - Directory paths (current dir, project root)
|
||||
//! - Machine hostname
|
||||
//! - Git information (branch name)
|
||||
@@ -158,6 +159,13 @@ pub(crate) enum StatusLineItem {
|
||||
|
||||
/// Latest checklist task progress from `update_plan` (if available).
|
||||
TaskProgress,
|
||||
|
||||
/// Named config profile selected for this invocation.
|
||||
Profile,
|
||||
|
||||
/// Model provider ID for the active thread.
|
||||
#[strum(to_string = "provider", serialize = "model-provider")]
|
||||
Provider,
|
||||
}
|
||||
|
||||
impl StatusLineItem {
|
||||
@@ -167,6 +175,10 @@ impl StatusLineItem {
|
||||
StatusLineItem::ModelName => "Current model name",
|
||||
StatusLineItem::ModelWithReasoning => "Current model name with reasoning level",
|
||||
StatusLineItem::Reasoning => "Current reasoning level",
|
||||
StatusLineItem::Profile => {
|
||||
"Config profile selected with --profile (omitted when unset)"
|
||||
}
|
||||
StatusLineItem::Provider => "Model provider for the active thread",
|
||||
StatusLineItem::CurrentDir => "Current working directory",
|
||||
StatusLineItem::ProjectRoot => "Project name (omitted when unavailable)",
|
||||
StatusLineItem::Hostname => "Current machine hostname (omitted when unavailable)",
|
||||
@@ -227,6 +239,8 @@ impl StatusLineItem {
|
||||
StatusLineItem::ModelName => StatusSurfacePreviewItem::Model,
|
||||
StatusLineItem::ModelWithReasoning => StatusSurfacePreviewItem::ModelWithReasoning,
|
||||
StatusLineItem::Reasoning => StatusSurfacePreviewItem::Reasoning,
|
||||
StatusLineItem::Profile => StatusSurfacePreviewItem::Profile,
|
||||
StatusLineItem::Provider => StatusSurfacePreviewItem::Provider,
|
||||
StatusLineItem::CurrentDir => StatusSurfacePreviewItem::CurrentDir,
|
||||
StatusLineItem::ProjectRoot => StatusSurfacePreviewItem::ProjectRoot,
|
||||
StatusLineItem::Hostname => StatusSurfacePreviewItem::Hostname,
|
||||
|
||||
@@ -38,6 +38,7 @@ impl StatusLineAccent {
|
||||
match item {
|
||||
StatusLineItem::ModelName
|
||||
| StatusLineItem::ModelWithReasoning
|
||||
| StatusLineItem::Provider
|
||||
| StatusLineItem::Reasoning => Self::Model,
|
||||
StatusLineItem::CurrentDir | StatusLineItem::ProjectRoot => Self::Path,
|
||||
StatusLineItem::GitBranch
|
||||
@@ -53,9 +54,10 @@ impl StatusLineAccent {
|
||||
| StatusLineItem::ThreadCredits
|
||||
| StatusLineItem::EstimatedThreadCost => Self::Usage,
|
||||
StatusLineItem::FiveHourLimit | StatusLineItem::WeeklyLimit => Self::Limit,
|
||||
StatusLineItem::CodexVersion | StatusLineItem::Hostname | StatusLineItem::SessionId => {
|
||||
Self::Metadata
|
||||
}
|
||||
StatusLineItem::CodexVersion
|
||||
| StatusLineItem::Hostname
|
||||
| StatusLineItem::SessionId
|
||||
| StatusLineItem::Profile => Self::Metadata,
|
||||
StatusLineItem::FastMode | StatusLineItem::Daybreak | StatusLineItem::RawOutput => {
|
||||
Self::Mode
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ pub(crate) enum StatusSurfacePreviewItem {
|
||||
Model,
|
||||
ModelWithReasoning,
|
||||
Reasoning,
|
||||
Profile,
|
||||
Provider,
|
||||
TaskProgress,
|
||||
}
|
||||
|
||||
@@ -78,6 +80,8 @@ impl StatusSurfacePreviewItem {
|
||||
StatusSurfacePreviewItem::Model => "gpt-5.2-codex",
|
||||
StatusSurfacePreviewItem::ModelWithReasoning => "gpt-5.2-codex medium",
|
||||
StatusSurfacePreviewItem::Reasoning => "medium",
|
||||
StatusSurfacePreviewItem::Profile => "profile: <name>",
|
||||
StatusSurfacePreviewItem::Provider => "provider: <id>",
|
||||
StatusSurfacePreviewItem::TaskProgress => "Tasks 0/0",
|
||||
}
|
||||
}
|
||||
@@ -116,6 +120,8 @@ impl StatusSurfacePreviewItem {
|
||||
Self::Model,
|
||||
Self::ModelWithReasoning,
|
||||
Self::Reasoning,
|
||||
Self::Profile,
|
||||
Self::Provider,
|
||||
Self::TaskProgress,
|
||||
]
|
||||
.into_iter()
|
||||
|
||||
+8
-8
@@ -7,14 +7,14 @@ expression: status_line_popup_snapshot(&mut chat)
|
||||
|
||||
Type to search
|
||||
|
||||
› [x] Use theme colors Apply colors from the active /theme
|
||||
› [x] Use theme colors Apply colors from the active /theme
|
||||
───────────────────────
|
||||
[x] project-name Project name (omitted when unavailable)
|
||||
[x] git-branch Current Git branch (omitted when unavailable)
|
||||
[x] thread-title Current thread title, or thread identifier when unnamed
|
||||
[ ] model Current model name
|
||||
[ ] model-with-reasoning Current model name with reasoning level
|
||||
[ ] reasoning Current reasoning level
|
||||
[x] project-name Project name (omitted when unavailable)
|
||||
[x] git-branch Current Git branch (omitted when unavailable)
|
||||
[x] thread-title Current thread title, or thread identifier when unnamed
|
||||
[x] profile Config profile selected with --profile (omitted when unset)
|
||||
[x] provider Model provider for the active thread
|
||||
[ ] model Current model name
|
||||
↓
|
||||
my-project · feat/awesome-feature · thread title
|
||||
my-project · feat/awesome-feature · thread title · profile: <name> · provider: <id>
|
||||
space toggle · ←/→ reorder · enter save · esc cancel
|
||||
|
||||
@@ -731,6 +731,16 @@ impl ChatWidget {
|
||||
StatusLineItem::ModelName => Some(self.model_display_name().to_string()),
|
||||
StatusLineItem::ModelWithReasoning => Some(self.model_with_reasoning_display_name()),
|
||||
StatusLineItem::Reasoning => Some(self.reasoning_display_name()),
|
||||
StatusLineItem::Profile => {
|
||||
let layer = self.config.config_layer_stack.get_active_user_layer()?;
|
||||
let ConfigLayerSource::User { profile, .. } = &layer.name else {
|
||||
return None;
|
||||
};
|
||||
profile.as_ref().map(|name| format!("profile: {name}"))
|
||||
}
|
||||
StatusLineItem::Provider => self
|
||||
.thread_id
|
||||
.map(|_| format!("provider: {}", self.config.model_provider_id)),
|
||||
StatusLineItem::CurrentDir => {
|
||||
Some(format_directory_display(
|
||||
self.status_line_cwd(),
|
||||
@@ -887,6 +897,8 @@ impl ChatWidget {
|
||||
StatusSurfacePreviewItem::Model => StatusLineItem::ModelName,
|
||||
StatusSurfacePreviewItem::ModelWithReasoning => StatusLineItem::ModelWithReasoning,
|
||||
StatusSurfacePreviewItem::Reasoning => StatusLineItem::Reasoning,
|
||||
StatusSurfacePreviewItem::Profile => StatusLineItem::Profile,
|
||||
StatusSurfacePreviewItem::Provider => StatusLineItem::Provider,
|
||||
};
|
||||
self.status_line_value_for_item(status_line_item)
|
||||
}
|
||||
|
||||
@@ -107,6 +107,9 @@ async fn session_header_uses_catalog_display_name_without_changing_model() {
|
||||
async fn session_and_settings_sync_server_provider_id() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;
|
||||
chat.config.model_provider.base_url = Some("https://local-provider.example/v1".to_string());
|
||||
chat.local_settings.tui.status_line = Some(vec!["profile".into(), "provider".into()]);
|
||||
chat.refresh_status_line();
|
||||
assert_eq!(status_line_text(&chat), None);
|
||||
let status = |chat: &mut ChatWidget| {
|
||||
chat.add_status_output(
|
||||
/*refreshing_rate_limits*/ false, /*request_id*/ None,
|
||||
@@ -138,6 +141,10 @@ async fn session_and_settings_sync_server_provider_id() {
|
||||
assert!(displayed.contains("Model provider:") && displayed.contains("server-ollama"));
|
||||
assert!(!displayed.contains("local-provider.example"));
|
||||
assert!(!displayed.contains("ChatGPT workspace:"));
|
||||
assert_eq!(
|
||||
status_line_text(&chat).as_deref(),
|
||||
Some("provider: server-ollama")
|
||||
);
|
||||
|
||||
let mut other = configured_thread_session(ThreadId::new());
|
||||
other.model_provider_id = "server-bedrock".to_string();
|
||||
@@ -149,6 +156,15 @@ async fn session_and_settings_sync_server_provider_id() {
|
||||
chat.handle_thread_session_quiet(first);
|
||||
assert_eq!(chat.config.model_provider_id, "server-ollama");
|
||||
|
||||
chat.config.config_layer_stack = chat
|
||||
.config
|
||||
.config_layer_stack
|
||||
.with_user_config_profile(
|
||||
&chat.config.codex_home.join("work.config.toml"),
|
||||
Some(&"work".parse().unwrap()),
|
||||
toml::Table::new().into(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut updated = thread_settings_for_test("gpt-5.4", first_id);
|
||||
updated.thread_settings.model_provider = "server-updated".to_string();
|
||||
chat.handle_server_notification(
|
||||
@@ -156,6 +172,10 @@ async fn session_and_settings_sync_server_provider_id() {
|
||||
/*replay_kind*/ None,
|
||||
);
|
||||
assert!(status(&mut chat).contains("server-updated"));
|
||||
insta::assert_snapshot!(
|
||||
status_line_text(&chat).unwrap(),
|
||||
@"profile: work · provider: server-updated"
|
||||
);
|
||||
}
|
||||
|
||||
fn start_safety_buffering_test_turn(
|
||||
|
||||
@@ -219,6 +219,8 @@ async fn status_line_setup_popup_hardcoded_only_snapshot() {
|
||||
"project-name".to_string(),
|
||||
"git-branch".to_string(),
|
||||
"thread-title".to_string(),
|
||||
"profile".to_string(),
|
||||
"provider".to_string(),
|
||||
]);
|
||||
|
||||
assert_chatwidget_snapshot!(
|
||||
|
||||
Reference in New Issue
Block a user