Files
codex/codex-rs/tui/src/managed_new_thread_defaults_tests.rs
T
hefuc-oai cf36c688b3 [codex] Use managed defaults for TUI threads (#30147)
## Why

#29683 exposes managed defaults for new-thread model settings through
`configRequirements/read` without applying them server-wide. The TUI is
an app-server client, so it should explicitly consume those defaults
when it creates a fresh thread.

This lets plain `codex` start on the managed model while preserving the
existing ability to change model settings within the thread.

## What changed

- Read `requirements.models.newThread` during TUI app-server bootstrap.
- Apply the managed model, reasoning effort, and service tier to the
initial fresh thread and subsequent `/new` or `/clear` threads.
- Keep explicit launch overrides above the managed defaults.
- Normalize the managed `fast` service tier to the `priority` request
value.
- Leave resumed and forked threads unchanged.

The application logic lives in a small TUI-only module; app-server
`thread/start` behavior remains unchanged for other clients.

## User experience

- Plain `codex` starts with the managed new-thread settings.
- A user can still change settings with `/model` or the existing
service-tier controls.
- Starting another fresh thread reapplies the managed defaults.
- Explicit launch choices such as `codex -m <model>` continue to win.

## Validation

- `just test -p codex-tui managed_new_thread_defaults`
- `just fix -p codex-tui`

Depends on #29683.
2026-06-26 19:27:31 +00:00

111 lines
3.5 KiB
Rust

use super::*;
use crate::legacy_core::config::ConfigBuilder;
use codex_protocol::openai_models::ReasoningEffort;
use pretty_assertions::assert_eq;
async fn test_config() -> Config {
let codex_home = tempfile::tempdir().expect("tempdir").keep();
ConfigBuilder::default()
.codex_home(codex_home)
.build()
.await
.expect("config")
}
fn defaults() -> NewThreadModelDefaults {
NewThreadModelDefaults {
model: Some("managed-model".to_string()),
model_reasoning_effort: Some(ReasoningEffort::High),
service_tier: Some("fast".to_string()),
}
}
#[tokio::test]
async fn applies_managed_defaults_to_a_new_thread_config() {
let mut actual = test_config().await;
actual.model = Some("configured-model".to_string());
actual.model_reasoning_effort = Some(ReasoningEffort::Low);
actual.service_tier = Some("flex".to_string());
let mut expected = actual.clone();
expected.model = Some("managed-model".to_string());
expected.model_reasoning_effort = Some(ReasoningEffort::High);
expected.service_tier = Some(ServiceTier::Fast.request_value().to_string());
apply_managed_new_thread_defaults(
&mut actual,
Some(&defaults()),
&[],
&ConfigOverrides::default(),
);
assert_eq!(actual, expected);
}
#[tokio::test]
async fn explicit_model_skips_managed_model_and_reasoning_effort() {
let mut actual = test_config().await;
actual.model = Some("explicit-model".to_string());
actual.model_reasoning_effort = None;
actual.service_tier = Some("flex".to_string());
let mut expected = actual.clone();
expected.service_tier = Some(ServiceTier::Fast.request_value().to_string());
let harness_overrides = ConfigOverrides {
model: Some("explicit-model".to_string()),
..ConfigOverrides::default()
};
apply_managed_new_thread_defaults(&mut actual, Some(&defaults()), &[], &harness_overrides);
assert_eq!(actual, expected);
}
#[tokio::test]
async fn explicit_reasoning_effort_skips_managed_model_and_reasoning_effort() {
let mut actual = test_config().await;
actual.model = Some("configured-model".to_string());
actual.model_reasoning_effort = Some(ReasoningEffort::Low);
actual.service_tier = Some("flex".to_string());
let mut expected = actual.clone();
expected.service_tier = Some(ServiceTier::Fast.request_value().to_string());
let cli_kv_overrides = vec![(
"model_reasoning_effort".to_string(),
TomlValue::String("low".to_string()),
)];
apply_managed_new_thread_defaults(
&mut actual,
Some(&defaults()),
&cli_kv_overrides,
&ConfigOverrides::default(),
);
assert_eq!(actual, expected);
}
#[tokio::test]
async fn explicit_launch_overrides_take_precedence() {
let mut actual = test_config().await;
actual.model = Some("explicit-model".to_string());
actual.model_reasoning_effort = Some(ReasoningEffort::Low);
actual.service_tier = Some("flex".to_string());
let expected = actual.clone();
let cli_kv_overrides = vec![(
"model_reasoning_effort".to_string(),
TomlValue::String("low".to_string()),
)];
let harness_overrides = ConfigOverrides {
model: Some("explicit-model".to_string()),
service_tier: Some(Some("flex".to_string())),
..ConfigOverrides::default()
};
apply_managed_new_thread_defaults(
&mut actual,
Some(&defaults()),
&cli_kv_overrides,
&harness_overrides,
);
assert_eq!(actual, expected);
}