[codex] Support model-defined reasoning efforts (#26444)

## Summary
- accept non-empty model-defined reasoning effort values while
preserving built-in effort behavior
- propagate the non-Copy effort type through core, app-server, TUI,
telemetry, and persistence call sites
- preserve string wire encoding and expose an open-string schema for
clients
- update model selection and shortcut behavior for model-advertised
effort values

## Root cause
`ReasoningEffort` gained a string-backed custom variant, so it could no
longer implement `Copy` or rely on derived closed-enum serialization.
Existing consumers still moved effort values from shared references and
assumed a fixed built-in value set.

## Validation
- `just fmt`
- Local tests and compilation were not run per request; relying on CI.
This commit is contained in:
Ahmed Ibrahim authored and GitHub committed 2026-06-04 13:36:24 -07:00
1 parent 4a70e0ac1b
commit 8ac304c299
97 files changed
+685 -581

No files matched your search

@@ -2405,13 +2405,54 @@ async fn model_reasoning_selection_popup_snapshot() {
set_chatgpt_auth(&mut chat);
chat.set_reasoning_effort(Some(ReasoningEffortConfig::High));
let preset = get_available_model(&chat, "gpt-5.4");
let mut preset = get_available_model(&chat, "gpt-5.4");
preset
.supported_reasoning_efforts
.push(ReasoningEffortPreset {
effort: ReasoningEffortConfig::Custom("max".to_string()),
description: "Maximum available reasoning".to_string(),
});
chat.open_reasoning_popup(preset);
let popup = render_bottom_popup(&chat, /*width*/ 80);
assert_chatwidget_snapshot!("model_reasoning_selection_popup", popup);
}
#[tokio::test]
async fn model_reasoning_selection_popup_applies_custom_effort() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.4")).await;
let custom_effort = ReasoningEffortConfig::Custom("max".to_string());
chat.set_reasoning_effort(Some(ReasoningEffortConfig::XHigh));
let mut preset = get_available_model(&chat, "gpt-5.4");
preset
.supported_reasoning_efforts
.push(ReasoningEffortPreset {
effort: custom_effort.clone(),
description: "Maximum available reasoning".to_string(),
});
chat.open_reasoning_popup(preset);
while rx.try_recv().is_ok() {}
chat.handle_key_event(KeyEvent::from(KeyCode::Down));
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
let selected_effort_events = std::iter::from_fn(|| rx.try_recv().ok())
.filter_map(|event| match event {
AppEvent::UpdateReasoningEffort(effort) => Some((None, effort)),
AppEvent::PersistModelSelection { model, effort } => Some((Some(model), effort)),
_ => None,
})
.collect::<Vec<_>>();
assert_eq!(
selected_effort_events,
vec![
(None, Some(custom_effort.clone())),
(Some("gpt-5.4".to_string()), Some(custom_effort)),
]
);
}
#[tokio::test]
async fn model_reasoning_selection_popup_extra_high_warning_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5.2")).await;