mirror of
https://github.com/openai/codex.git
synced 2026-04-24 06:35:50 +00:00
docs(tui): clarify scoped model flow
Document the scoped model-selection flow in the TUI and config helpers added for `/model plan` and `/model default`.
This commit is contained in:
@@ -868,6 +868,11 @@ impl ConfigEditsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set or clear the Plan-mode-specific model override in `config.toml`.
|
||||
///
|
||||
/// When `model` is `Some`, the `plan_mode_model` key is written (or
|
||||
/// scoped under the active profile). When `None`, the key is removed so
|
||||
/// Plan mode falls back to the global `model` default.
|
||||
pub fn set_plan_mode_model(mut self, model: Option<&str>) -> Self {
|
||||
let segments = if let Some(profile) = self.profile.as_ref() {
|
||||
vec![
|
||||
|
||||
@@ -70,6 +70,12 @@ pub(crate) enum WindowsSandboxEnableMode {
|
||||
Legacy,
|
||||
}
|
||||
|
||||
/// Where a model selection from the picker should be applied.
|
||||
///
|
||||
/// The TUI model picker can target a specific collaboration mode without
|
||||
/// requiring the user to switch into that mode first. `Active` preserves the
|
||||
/// legacy behavior (mutate whatever is current); `Default` and `Plan` allow
|
||||
/// cross-mode selection, e.g. `/model plan` while in Default mode.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum ModelSelectionTarget {
|
||||
/// Apply a model selection to whichever collaboration mode is active now.
|
||||
@@ -81,6 +87,9 @@ pub(crate) enum ModelSelectionTarget {
|
||||
}
|
||||
|
||||
impl ModelSelectionTarget {
|
||||
/// Resolve the abstract target into a concrete [`ModeKind`] given the
|
||||
/// currently active mode. `Active` is the only variant that depends on
|
||||
/// runtime state; the others are constant.
|
||||
pub(crate) fn mode_kind(self, active_mode: ModeKind) -> ModeKind {
|
||||
match self {
|
||||
Self::Active => active_mode,
|
||||
|
||||
@@ -255,10 +255,20 @@ const PLAN_MODE_REASONING_SCOPE_ALL_MODES: &str = "Apply to global default and P
|
||||
const CONNECTORS_SELECTION_VIEW_ID: &str = "connectors-selection";
|
||||
const TUI_STUB_MESSAGE: &str = "Not available in TUI yet.";
|
||||
|
||||
/// The outcome of resolving a [`ModelSelectionTarget`] against the current
|
||||
/// widget state.
|
||||
///
|
||||
/// This sits between the user-facing target (which mode they *want* to
|
||||
/// change) and the event dispatch (which `AppEvent`s to send). The
|
||||
/// `PromptPlanScope` variant triggers the existing "apply to plan only vs.
|
||||
/// all modes" confirmation dialog before committing the selection.
|
||||
#[derive(Clone, Copy)]
|
||||
enum ModelSelectionResolution {
|
||||
/// Persist as the Default-mode model and reasoning effort.
|
||||
UpdateDefault,
|
||||
/// Persist as the Plan-mode model override and reasoning effort.
|
||||
UpdatePlan,
|
||||
/// Show the Plan-mode scope prompt before persisting.
|
||||
PromptPlanScope,
|
||||
}
|
||||
|
||||
@@ -5495,6 +5505,11 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Drain the composer's pending submission state after a slash command
|
||||
/// with inline args has been accepted and the popup opened.
|
||||
///
|
||||
/// Without this, the composer would still hold the `/model plan` text and
|
||||
/// treat the next Enter as a duplicate submission.
|
||||
fn consume_accepted_inline_args_command(&mut self) {
|
||||
let Some((_prepared_args, _prepared_elements)) = self
|
||||
.bottom_pane
|
||||
@@ -7707,6 +7722,10 @@ impl ChatWidget {
|
||||
self.open_model_popup_for_target(ModelSelectionTarget::Active);
|
||||
}
|
||||
|
||||
/// Open the model picker popup scoped to a specific selection target.
|
||||
///
|
||||
/// The target determines which mode's current model is highlighted and
|
||||
/// which persistence path is used when the user confirms a selection.
|
||||
pub(crate) fn open_model_popup_for_target(&mut self, target: ModelSelectionTarget) {
|
||||
if !self.is_session_configured() {
|
||||
self.add_info_message(
|
||||
@@ -7968,6 +7987,8 @@ impl ChatWidget {
|
||||
Box::new(header)
|
||||
}
|
||||
|
||||
/// Append a mode-scope suffix to the popup title when the selection targets
|
||||
/// a specific mode (e.g. "Select Model for Plan Mode").
|
||||
fn model_menu_title(&self, target: ModelSelectionTarget, base_title: &str) -> String {
|
||||
match target {
|
||||
ModelSelectionTarget::Active => base_title.to_string(),
|
||||
@@ -7976,6 +7997,7 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a mode-scope clarification to the popup subtitle.
|
||||
fn model_menu_subtitle(&self, target: ModelSelectionTarget, base_subtitle: &str) -> String {
|
||||
match target {
|
||||
ModelSelectionTarget::Active => base_subtitle.to_string(),
|
||||
@@ -8217,6 +8239,12 @@ impl ChatWidget {
|
||||
});
|
||||
}
|
||||
|
||||
/// Build the closure(s) that fire when the user confirms a model selection.
|
||||
///
|
||||
/// The returned actions send the appropriate `AppEvent` sequence for the
|
||||
/// given [`ModelSelectionResolution`]. This is a pure factory — it captures
|
||||
/// the resolution at popup-creation time so the popup callback doesn't need
|
||||
/// access to widget state.
|
||||
fn model_selection_actions(
|
||||
model_for_action: String,
|
||||
effort_for_action: Option<ReasoningEffortConfig>,
|
||||
@@ -8248,6 +8276,13 @@ impl ChatWidget {
|
||||
})]
|
||||
}
|
||||
|
||||
/// Decide how a model selection should be applied given the target and the
|
||||
/// current widget state.
|
||||
///
|
||||
/// For explicit `Default`/`Plan` targets the mapping is trivial. For
|
||||
/// `Active`, the function consults `should_prompt_plan_mode_reasoning_scope`
|
||||
/// (which checks whether the effort differs from the Plan preset default)
|
||||
/// and falls back to whichever mode is currently active.
|
||||
fn model_selection_resolution(
|
||||
&self,
|
||||
target: ModelSelectionTarget,
|
||||
@@ -8549,6 +8584,12 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch the appropriate update + persist events for a confirmed model
|
||||
/// selection.
|
||||
///
|
||||
/// This is the non-closure counterpart of [`model_selection_actions`] —
|
||||
/// used when the widget can send events directly (e.g. single-effort
|
||||
/// models that skip the reasoning popup).
|
||||
fn apply_model_selection(
|
||||
&self,
|
||||
target: ModelSelectionTarget,
|
||||
@@ -9677,6 +9718,16 @@ impl ChatWidget {
|
||||
self.model_for_selection_target(ModelSelectionTarget::Active)
|
||||
}
|
||||
|
||||
/// Resolve the "current" model for the given selection target.
|
||||
///
|
||||
/// For Plan mode the precedence is:
|
||||
/// 1. `config.plan_mode_model` (explicit user override),
|
||||
/// 2. the active collaboration mask's model (only when the mask is
|
||||
/// currently Plan — ignored if the user targeted Plan from Default
|
||||
/// mode, since the mask belongs to Default in that case),
|
||||
/// 3. the global default model.
|
||||
///
|
||||
/// For all other modes the global default model is used directly.
|
||||
fn model_for_selection_target(&self, target: ModelSelectionTarget) -> &str {
|
||||
if !self.collaboration_modes_enabled() {
|
||||
return self.current_collaboration_mode.model();
|
||||
@@ -9687,6 +9738,9 @@ impl ChatWidget {
|
||||
.plan_mode_model
|
||||
.as_deref()
|
||||
.or_else(|| {
|
||||
// Only consult the active mask when it *is* Plan mode.
|
||||
// If the target is Plan but the active mask is Default,
|
||||
// the mask's model belongs to Default, not Plan.
|
||||
self.active_collaboration_mask
|
||||
.as_ref()
|
||||
.filter(|mask| mask.mode == Some(ModeKind::Plan))
|
||||
@@ -9830,6 +9884,11 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the Plan-mode reasoning effort that the picker should treat as
|
||||
/// "current."
|
||||
///
|
||||
/// Precedence: active Plan mask effort > `config.plan_mode_reasoning_effort`
|
||||
/// > catalog Plan preset default.
|
||||
fn plan_mode_reasoning_effort_for_picker(&self) -> Option<ReasoningEffortConfig> {
|
||||
if !self.collaboration_modes_enabled() {
|
||||
return self.current_collaboration_mode.reasoning_effort();
|
||||
|
||||
Reference in New Issue
Block a user