Skip to content

Commit 4745242

Browse files
committed
fix(codex): adjustable reasoning levels (low/medium/high/xhigh) in picker --patch
The model picker only showed the ←/→ effort selector for Claude models, so Codex/GPT-5 users had no way to pick a thinking level. Enable it for the GPT-5 reasoning family (gpt-5.5/5.4/5.4-mini/codex), excluding the non-reasoning -chat/-pro snapshots. The four tiers map to OpenAI reasoning_effort low/medium/high, and on Codex the top "Max" tier becomes xhigh ("extra high") to match opencode's gpt-5 ladder — scoped to Codex so other OpenAI-compatible providers (which may reject xhigh) keep "high". Also set reasoningSummary=auto + reasoning.encrypted_content include for Codex, matching opencode's gpt-5 defaults. Ships as a patch to v0.1.6.
1 parent e9817eb commit 4745242

2 files changed

Lines changed: 85 additions & 1 deletion

File tree

src-rust/crates/query/src/lib.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,11 +363,34 @@ fn build_provider_options(
363363
let reasoning_effort = effort_level
364364
.map(reasoning_effort_for_level)
365365
.unwrap_or("medium");
366+
// Codex (ChatGPT) accepts the full gpt-5 effort ladder including
367+
// `xhigh`, so surface the top "Max" tier as "extra high" there —
368+
// matching opencode — without changing the value sent to other
369+
// OpenAI-compatible providers that may not accept it.
370+
let reasoning_effort = if matches!(provider_id, "codex" | "openai-codex")
371+
&& effort_level == Some(claurst_core::effort::EffortLevel::Max)
372+
{
373+
"xhigh"
374+
} else {
375+
reasoning_effort
376+
};
366377
options.insert(
367378
"reasoningEffort".to_string(),
368379
serde_json::json!(reasoning_effort),
369380
);
370381

382+
// Match opencode's gpt-5 defaults for the Codex (ChatGPT) endpoint:
383+
// request an auto reasoning summary and carry encrypted reasoning state
384+
// across stateless turns. Scoped to Codex so other OpenAI-compatible
385+
// providers that ignore these fields are unaffected.
386+
if matches!(provider_id, "codex" | "openai-codex") {
387+
options.insert("reasoningSummary".to_string(), serde_json::json!("auto"));
388+
options.insert(
389+
"include".to_string(),
390+
serde_json::json!(["reasoning.encrypted_content"]),
391+
);
392+
}
393+
371394
if model_id.starts_with("gpt-5")
372395
&& model_id.contains("gpt-5.")
373396
&& !model_id.contains("codex")
@@ -2446,6 +2469,37 @@ mod tests {
24462469
assert_eq!(options["usage"]["include"], serde_json::json!(true));
24472470
}
24482471

2472+
#[test]
2473+
fn test_build_provider_options_codex_effort_ladder() {
2474+
// Codex maps the lower tiers like any OpenAI reasoning model...
2475+
for (level, expected) in [
2476+
(claurst_core::effort::EffortLevel::Low, "low"),
2477+
(claurst_core::effort::EffortLevel::Medium, "medium"),
2478+
(claurst_core::effort::EffortLevel::High, "high"),
2479+
] {
2480+
let options = build_provider_options("openai-codex", "gpt-5.5", Some(level), None);
2481+
assert_eq!(options["reasoningEffort"], serde_json::json!(expected));
2482+
}
2483+
// ...but the top "Max" tier becomes "xhigh" (extra high) on Codex.
2484+
let options = build_provider_options(
2485+
"openai-codex",
2486+
"gpt-5.5",
2487+
Some(claurst_core::effort::EffortLevel::Max),
2488+
None,
2489+
);
2490+
assert_eq!(options["reasoningEffort"], serde_json::json!("xhigh"));
2491+
assert_eq!(options["reasoningSummary"], serde_json::json!("auto"));
2492+
2493+
// Other OpenAI-compatible providers keep "high" for Max (no xhigh).
2494+
let other = build_provider_options(
2495+
"openrouter",
2496+
"gpt-5.4",
2497+
Some(claurst_core::effort::EffortLevel::Max),
2498+
None,
2499+
);
2500+
assert_eq!(other["reasoningEffort"], serde_json::json!("high"));
2501+
}
2502+
24492503
#[test]
24502504
fn test_build_provider_options_for_bedrock_anthropic() {
24512505
let options = build_provider_options(

src-rust/crates/tui/src/model_picker.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,31 @@ impl Default for EffortLevel {
9090
// ---------------------------------------------------------------------------
9191

9292
/// Returns `true` for models that support extended thinking / effort levels.
93+
///
94+
/// Covers Claude's extended-thinking models and the GPT-5 reasoning family
95+
/// (incl. Codex / ChatGPT models like `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`),
96+
/// which honour OpenAI's `reasoning_effort` ladder. Lets the picker surface the
97+
/// ←/→ thinking-level selector for Codex the way opencode does.
9398
pub fn model_supports_effort(id: &str) -> bool {
9499
id.starts_with("claude-3-7")
95100
|| id.starts_with("claude-opus-4")
96101
|| id.starts_with("claude-sonnet-4")
102+
|| is_gpt5_reasoning_model(id)
97103
}
98104

99105
/// Returns `true` for models that support the maximum effort tier.
106+
///
107+
/// For Claude this is Opus-only; for the GPT-5 family the top tier maps to
108+
/// OpenAI's `xhigh` ("extra high") reasoning effort on the Codex endpoint.
100109
pub fn model_supports_max_effort(id: &str) -> bool {
101-
id.starts_with("claude-opus-4")
110+
id.starts_with("claude-opus-4") || is_gpt5_reasoning_model(id)
111+
}
112+
113+
/// Whether `id` is a GPT-5 reasoning model (`gpt-5*`), excluding the
114+
/// non-reasoning chat / pro snapshots that ignore `reasoning_effort`.
115+
fn is_gpt5_reasoning_model(id: &str) -> bool {
116+
let id = id.to_ascii_lowercase();
117+
id.starts_with("gpt-5") && !id.contains("-chat") && !id.contains("-pro")
102118
}
103119

104120
/// Returns a short description string based on the model family inferred from
@@ -1162,6 +1178,20 @@ mod tests {
11621178
assert!(!model_supports_max_effort("claude-haiku-4-5"));
11631179
}
11641180

1181+
// 12b. GPT-5 codex/reasoning models expose the effort selector (incl. max).
1182+
#[test]
1183+
fn gpt5_codex_models_support_effort() {
1184+
for id in ["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark"] {
1185+
assert!(model_supports_effort(id), "{id} should support effort");
1186+
assert!(model_supports_max_effort(id), "{id} should support max/xhigh");
1187+
}
1188+
// Non-reasoning chat / pro snapshots are excluded.
1189+
assert!(!model_supports_effort("gpt-5-chat-latest"));
1190+
assert!(!model_supports_effort("gpt-5.5-pro"));
1191+
// Non-gpt5 stays unaffected.
1192+
assert!(!model_supports_effort("gpt-4o"));
1193+
}
1194+
11651195
// 13. Non-effort models return None from effective_effort.
11661196
#[test]
11671197
fn haiku_has_no_effort() {

0 commit comments

Comments
 (0)