Skip to content

Commit cd3233d

Browse files
author
Tanger TAS
committed
feat(reasoning): add max effort
1 parent 3e1c420 commit cd3233d

13 files changed

Lines changed: 66 additions & 31 deletions

File tree

crates/config/src/schema.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ pub enum ReasoningEffort {
6262
/// Extra-high reasoning effort. Serializes as `"xhigh"`.
6363
#[serde(rename = "xhigh")]
6464
ExtraHigh,
65+
/// Maximum reasoning effort. Serializes as `"max"`.
66+
Max,
6567
}
6668

6769
impl ReasoningEffort {
@@ -72,6 +74,7 @@ impl ReasoningEffort {
7274
Self::Medium,
7375
Self::High,
7476
Self::ExtraHigh,
77+
Self::Max,
7578
];
7679

7780
/// Wire / serialization name (matches serde output).
@@ -83,6 +86,7 @@ impl ReasoningEffort {
8386
Self::Medium => "medium",
8487
Self::High => "high",
8588
Self::ExtraHigh => "xhigh",
89+
Self::Max => "max",
8690
}
8791
}
8892

@@ -95,6 +99,7 @@ impl ReasoningEffort {
9599
Self::Medium => "Medium",
96100
Self::High => "High",
97101
Self::ExtraHigh => "Extra High",
102+
Self::Max => "Max",
98103
}
99104
}
100105
}
@@ -109,6 +114,7 @@ impl TryFrom<&str> for ReasoningEffort {
109114
"medium" => Ok(Self::Medium),
110115
"high" => Ok(Self::High),
111116
"xhigh" => Ok(Self::ExtraHigh),
117+
"max" => Ok(Self::Max),
112118
other => Err(format!("unknown reasoning effort: {other}")),
113119
}
114120
}

crates/config/src/validate/tests/agents.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ max_iterations = 0
125125

126126
#[test]
127127
fn reasoning_effort_valid_values_no_error() {
128-
for effort in &["minimal", "low", "medium", "high", "xhigh"] {
128+
for effort in &["minimal", "low", "medium", "high", "xhigh", "max"] {
129129
let toml = format!(
130130
r#"
131131
[agents.presets.thinker]

crates/providers/src/anthropic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ impl AnthropicProvider {
111111
ReasoningEffort::Low => 4096,
112112
ReasoningEffort::Medium => 10240,
113113
ReasoningEffort::High => 32768,
114-
ReasoningEffort::ExtraHigh => 65536,
114+
ReasoningEffort::ExtraHigh | ReasoningEffort::Max => 65536,
115115
};
116116
body["thinking"] = serde_json::json!({
117117
"type": "enabled",

crates/providers/src/model_id.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub(crate) const REASONING_SUFFIXES: &[(&str, moltis_agents::model::ReasoningEff
2727
"reasoning-xhigh",
2828
moltis_agents::model::ReasoningEffort::ExtraHigh,
2929
),
30+
("reasoning-max", moltis_agents::model::ReasoningEffort::Max),
3031
];
3132

3233
#[must_use]
@@ -106,6 +107,10 @@ mod tests {
106107
split_reasoning_suffix("gpt-5@reasoning-xhigh"),
107108
("gpt-5", Some(ReasoningEffort::ExtraHigh))
108109
);
110+
assert_eq!(
111+
split_reasoning_suffix("gpt-5.6-sol@reasoning-max"),
112+
("gpt-5.6-sol", Some(ReasoningEffort::Max))
113+
);
109114
assert_eq!(split_reasoning_suffix("gpt-4o"), ("gpt-4o", None));
110115
assert_eq!(
111116
split_reasoning_suffix("model@unknown-suffix"),
@@ -132,6 +137,10 @@ mod tests {
132137
"claude-opus-4-5"
133138
);
134139
assert_eq!(raw_model_id("o3@reasoning-medium"), "o3");
140+
assert_eq!(
141+
raw_model_id("openai-codex::gpt-5.6-sol@reasoning-max"),
142+
"gpt-5.6-sol"
143+
);
135144
assert_eq!(raw_model_id("gpt-4o"), "gpt-4o");
136145
}
137146
}

crates/providers/src/openai/provider/core.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ impl OpenAiProvider {
260260
| ReasoningEffort::Low
261261
| ReasoningEffort::Medium
262262
| ReasoningEffort::High => "high",
263-
ReasoningEffort::ExtraHigh => "max",
263+
ReasoningEffort::ExtraHigh | ReasoningEffort::Max => "max",
264264
}),
265265
ReasoningEffortPolicy::KimiMax => self.reasoning_effort.map(|_| "max"),
266266
ReasoningEffortPolicy::OpenAi => self.reasoning_effort.map(|e| match e {
@@ -274,10 +274,11 @@ impl OpenAiProvider {
274274
ReasoningEffort::Low => "low",
275275
ReasoningEffort::Medium => "medium",
276276
ReasoningEffort::High => "high",
277-
ReasoningEffort::ExtraHigh => {
277+
ReasoningEffort::ExtraHigh | ReasoningEffort::Max => {
278278
tracing::debug!(
279279
model = %self.model,
280-
"reasoning effort ExtraHigh clamped to \"high\" (OpenAI maximum)"
280+
effort = e.as_str(),
281+
"reasoning effort clamped to \"high\" (OpenAI Chat Completions maximum)"
281282
);
282283
"high"
283284
},

crates/providers/src/openai/provider/request.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -958,22 +958,27 @@ mod tests {
958958
}
959959

960960
#[test]
961-
fn deepseek_v4_reasoning_effort_enables_thinking_and_maps_xhigh_to_max() {
962-
let mut p = provider("deepseek-v4-pro", "deepseek", "https://api.deepseek.com")
963-
.with_capabilities(OpenAiProviderCapabilities {
964-
reasoning_effort_policy: ReasoningEffortPolicy::DeepSeek,
965-
..OpenAiProviderCapabilities::DEFAULT
961+
fn deepseek_v4_reasoning_effort_enables_thinking_and_maps_top_levels_to_max() {
962+
for effort in [
963+
moltis_agents::model::ReasoningEffort::ExtraHigh,
964+
moltis_agents::model::ReasoningEffort::Max,
965+
] {
966+
let mut p = provider("deepseek-v4-pro", "deepseek", "https://api.deepseek.com")
967+
.with_capabilities(OpenAiProviderCapabilities {
968+
reasoning_effort_policy: ReasoningEffortPolicy::DeepSeek,
969+
..OpenAiProviderCapabilities::DEFAULT
970+
});
971+
p.reasoning_effort = Some(effort);
972+
let mut body = serde_json::json!({
973+
"model": "deepseek-v4-pro",
974+
"messages": [{"role": "user", "content": "hello"}],
966975
});
967-
p.reasoning_effort = Some(moltis_agents::model::ReasoningEffort::ExtraHigh);
968-
let mut body = serde_json::json!({
969-
"model": "deepseek-v4-pro",
970-
"messages": [{"role": "user", "content": "hello"}],
971-
});
972976

973-
p.apply_reasoning_effort_chat(&mut body);
977+
p.apply_reasoning_effort_chat(&mut body);
974978

975-
assert_eq!(body["reasoning_effort"], "max");
976-
assert_eq!(body["thinking"], serde_json::json!({ "type": "enabled" }));
979+
assert_eq!(body["reasoning_effort"], "max");
980+
assert_eq!(body["thinking"], serde_json::json!({ "type": "enabled" }));
981+
}
977982
}
978983

979984
#[test]
@@ -1002,6 +1007,7 @@ mod tests {
10021007
moltis_agents::model::ReasoningEffort::Medium,
10031008
moltis_agents::model::ReasoningEffort::High,
10041009
moltis_agents::model::ReasoningEffort::ExtraHigh,
1010+
moltis_agents::model::ReasoningEffort::Max,
10051011
] {
10061012
let mut p = provider("kimi-k3", "moonshot", "https://api.moonshot.ai/v1")
10071013
.with_capabilities(OpenAiProviderCapabilities {

crates/providers/src/openai_codex.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl OpenAiCodexProvider {
102102

103103
/// Add `reasoning.effort` to a Responses-API request body when an effort
104104
/// is configured. GPT-5 family models accept `minimal`, `low`, `medium`,
105-
/// `high`, and `xhigh` (matching the official Codex client).
105+
/// `high`, `xhigh`, and `max` (matching the current Codex model catalog).
106106
fn apply_reasoning(&self, body: &mut serde_json::Value) {
107107
if let Some(effort) = self.reasoning_effort {
108108
body["reasoning"]["effort"] = serde_json::json!(effort.as_str());
@@ -947,6 +947,7 @@ mod tests {
947947
(ReasoningEffort::Medium, "medium"),
948948
(ReasoningEffort::High, "high"),
949949
(ReasoningEffort::ExtraHigh, "xhigh"),
950+
(ReasoningEffort::Max, "max"),
950951
] {
951952
let mut provider = OpenAiCodexProvider::new("gpt-5.4".to_string());
952953
provider.reasoning_effort = Some(effort);

crates/web/ui/e2e/specs/reasoning-toggle.spec.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ test.describe("reasoning effort toggle", () => {
6464
expect(pageErrors).toEqual([]);
6565
});
6666

67-
test("clicking toggle opens dropdown with Off/Low/Medium/High options", async ({ page }) => {
67+
test("clicking toggle opens dropdown with all reasoning effort options", async ({ page }) => {
6868
const pageErrors = watchPageErrors(page);
6969

7070
await setMockModels(
@@ -81,13 +81,14 @@ test.describe("reasoning effort toggle", () => {
8181
await expect(dropdown).toBeVisible();
8282

8383
const items = page.locator("#reasoningDropdownList .model-dropdown-item");
84-
await expect(items).toHaveCount(6);
84+
await expect(items).toHaveCount(7);
8585
await expect(items.nth(0)).toHaveText("Off");
8686
await expect(items.nth(1)).toHaveText("Minimal");
8787
await expect(items.nth(2)).toHaveText("Low");
8888
await expect(items.nth(3)).toHaveText("Medium");
8989
await expect(items.nth(4)).toHaveText("High");
9090
await expect(items.nth(5)).toHaveText("Extra High");
91+
await expect(items.nth(6)).toHaveText("Max");
9192

9293
expect(pageErrors).toEqual([]);
9394
});
@@ -141,12 +142,19 @@ test.describe("reasoning effort toggle", () => {
141142
window.__chatWsSpyInstalled = true;
142143
});
143144

144-
// Set up a reasoning model and select high effort
145+
// Set up a reasoning model and select maximum effort
145146
await setMockModels(
146147
page,
147-
[{ id: "claude-opus-4-5", displayName: "Claude Opus 4.5", provider: "anthropic", supportsReasoning: true }],
148-
"claude-opus-4-5",
149-
"high",
148+
[
149+
{
150+
id: "openai-codex::gpt-5.6-sol",
151+
displayName: "GPT-5.6 Sol",
152+
provider: "openai-codex",
153+
supportsReasoning: true,
154+
},
155+
],
156+
"openai-codex::gpt-5.6-sol",
157+
"max",
150158
);
151159

152160
const chatInput = page.locator("#chatInput");
@@ -155,7 +163,7 @@ test.describe("reasoning effort toggle", () => {
155163

156164
const payloads = await page.evaluate(() => window.__chatSendPayloads);
157165
expect(payloads.length).toBeGreaterThan(0);
158-
expect(payloads[0].model).toBe("claude-opus-4-5@reasoning-high");
166+
expect(payloads[0].model).toBe("openai-codex::gpt-5.6-sol@reasoning-max");
159167

160168
expect(pageErrors).toEqual([]);
161169
});

crates/web/ui/src/locales/en/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export default {
5252
reasoningMedium: "Medium",
5353
reasoningHigh: "High",
5454
reasoningExtraHigh: "Extra High",
55+
reasoningMax: "Max",
5556

5657
// ── Debug panel ──────────────────────────────────────────
5758
debugTooltip: "Show context debug info",

crates/web/ui/src/locales/fr/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export default {
5252
reasoningMedium: "Moyen",
5353
reasoningHigh: "Élevé",
5454
reasoningExtraHigh: "Très élevé",
55+
reasoningMax: "Maximum",
5556

5657
// ── Debug panel ──────────────────────────────────────────
5758
debugTooltip: "Show context debug info",

0 commit comments

Comments
 (0)