Show reasoning effort in the model's own vocabulary (K3: max, not xhigh)
The welcome and prompt model labels (and /effort's 'current' hint) rendered the canonical internal level name, so K3 at its default effort showed 'K3 (xhigh)' even though the server's vocabulary for that level is 'max' (live /models think_efforts: low/high/max). New ModelState::reasoning_effort_display() resolves the current effort through the model's own effort menu (option id whose value matches), falling back to the canonical name only when the model has no entry for the level. All three display sites route through it; test pins the K3 mapping (Xhigh → 'max', Low → 'low', no-menu-entry → canonical).
This commit is contained in:
@@ -204,6 +204,22 @@ impl ModelState {
|
||||
parse_reasoning_efforts_meta(info.meta.as_ref()).unwrap_or_else(legacy_effort_options)
|
||||
}
|
||||
|
||||
/// Display token for the current reasoning effort, in the MODEL's own
|
||||
/// vocabulary: the menu option id whose value matches (e.g. K3 spells
|
||||
/// `Xhigh` as `max`), falling back to the canonical name only when the
|
||||
/// model has no menu entry for it. Displays must never leak the internal
|
||||
/// canonical spelling for a level the server names differently.
|
||||
pub fn reasoning_effort_display(&self) -> Option<String> {
|
||||
let effort = self.reasoning_effort?;
|
||||
let display = self
|
||||
.reasoning_effort_options()
|
||||
.into_iter()
|
||||
.find(|opt| opt.value == effort)
|
||||
.map(|opt| opt.id)
|
||||
.unwrap_or_else(|| effort.as_str().to_owned());
|
||||
Some(display)
|
||||
}
|
||||
|
||||
/// Map a typed/selected effort token to its canonical value for the current
|
||||
/// model. Accepts a menu option id (case-insensitive) or a canonical level
|
||||
/// that appears as a **value** in that model's menu. Levels the model does
|
||||
@@ -479,6 +495,30 @@ mod tests {
|
||||
assert_eq!(opts[1].description.as_deref(), Some("Max"));
|
||||
}
|
||||
|
||||
/// K3 spells `Xhigh` as `max` (live /models `think_efforts`): every
|
||||
/// display surface must show the model's own vocabulary, never the
|
||||
/// internal canonical name.
|
||||
#[test]
|
||||
fn reasoning_effort_display_uses_model_vocabulary() {
|
||||
let mut state = state_with_meta(Some(serde_json::json!({
|
||||
"supportsReasoningEffort": true,
|
||||
"reasoningEfforts": [
|
||||
{ "id": "low", "value": "low", "label": "Low" },
|
||||
{ "id": "high", "value": "high", "label": "High" },
|
||||
{ "id": "max", "value": "xhigh", "label": "Max" },
|
||||
],
|
||||
})));
|
||||
state.reasoning_effort = Some(ReasoningEffort::Xhigh);
|
||||
assert_eq!(state.reasoning_effort_display().as_deref(), Some("max"));
|
||||
state.reasoning_effort = Some(ReasoningEffort::Low);
|
||||
assert_eq!(state.reasoning_effort_display().as_deref(), Some("low"));
|
||||
// No menu entry for the level → canonical fallback (never hide it).
|
||||
state.reasoning_effort = Some(ReasoningEffort::Medium);
|
||||
assert_eq!(state.reasoning_effort_display().as_deref(), Some("medium"));
|
||||
state.reasoning_effort = None;
|
||||
assert_eq!(state.reasoning_effort_display(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reasoning_effort_options_gate_first_empty_when_unsupported() {
|
||||
// No current model → empty.
|
||||
|
||||
@@ -2038,7 +2038,7 @@ impl AgentView {
|
||||
}
|
||||
let mode_flags: &[PromptFlag] = &mode_flags_vec;
|
||||
let multiline = self.multiline_mode;
|
||||
let model_label = match self.session.models.reasoning_effort {
|
||||
let model_label = match self.session.models.reasoning_effort_display() {
|
||||
Some(eff) => format!("{model_id} ({eff})"),
|
||||
None => model_id,
|
||||
};
|
||||
|
||||
@@ -3060,7 +3060,7 @@ impl AppView {
|
||||
self.tip.as_deref()
|
||||
};
|
||||
let model_name_base = self.models.current_model_name().unwrap_or_default();
|
||||
let model_name = match self.models.reasoning_effort {
|
||||
let model_name = match self.models.reasoning_effort_display() {
|
||||
Some(eff) => format!("{model_name_base} ({eff})"),
|
||||
None => model_name_base,
|
||||
};
|
||||
|
||||
@@ -69,7 +69,7 @@ impl SlashCommand for EffortCommand {
|
||||
.collect();
|
||||
let current = ctx
|
||||
.models
|
||||
.reasoning_effort
|
||||
.reasoning_effort_display()
|
||||
.map(|e| format!(" (current: {e})"))
|
||||
.unwrap_or_default();
|
||||
let levels = if offered.is_empty() {
|
||||
|
||||
Reference in New Issue
Block a user