From 65cda3c96c0ebb8add8fa59a1efd206fd90e43e6 Mon Sep 17 00:00:00 2001 From: jh-block Date: Wed, 20 May 2026 14:26:44 +0200 Subject: [PATCH] Add xhigh thinking effort Signed-off-by: jh-block --- crates/goose/src/model.rs | 11 +++++---- crates/goose/src/providers/chatgpt_codex.rs | 1 + crates/goose/src/providers/codex.rs | 3 ++- .../goose/src/providers/formats/anthropic.rs | 24 ++++++++++++++++--- .../goose/src/providers/formats/databricks.rs | 24 +++++++++---------- crates/goose/src/providers/formats/google.rs | 4 +++- .../goose/src/providers/formats/openrouter.rs | 1 + crates/goose/src/providers/utils.rs | 1 + ui/desktop/openapi.json | 1 + ui/desktop/src/api/types.gen.ts | 2 +- .../models/subcomponents/SwitchModelModal.tsx | 5 ++++ ui/desktop/src/i18n/messages/en.json | 3 +++ ui/desktop/src/i18n/messages/zh-CN.json | 3 +++ 13 files changed, 61 insertions(+), 22 deletions(-) diff --git a/crates/goose/src/model.rs b/crates/goose/src/model.rs index eeb54562ff..02b366a4c2 100644 --- a/crates/goose/src/model.rs +++ b/crates/goose/src/model.rs @@ -17,6 +17,7 @@ pub enum ThinkingEffort { Low, Medium, High, + XHigh, Max, } @@ -28,7 +29,8 @@ impl FromStr for ThinkingEffort { "low" => Ok(Self::Low), "medium" | "med" => Ok(Self::Medium), "high" => Ok(Self::High), - "max" | "xhigh" => Ok(Self::Max), + "xhigh" => Ok(Self::XHigh), + "max" => Ok(Self::Max), other => Err(format!("unknown thinking effort: '{other}'")), } } @@ -41,6 +43,7 @@ impl fmt::Display for ThinkingEffort { Self::Low => write!(f, "low"), Self::Medium => write!(f, "medium"), Self::High => write!(f, "high"), + Self::XHigh => write!(f, "xhigh"), Self::Max => write!(f, "max"), } } @@ -445,7 +448,7 @@ impl ModelConfig { "low" => ThinkingEffort::Low, "medium" => ThinkingEffort::Medium, "high" => ThinkingEffort::High, - "xhigh" => ThinkingEffort::Max, + "xhigh" => ThinkingEffort::XHigh, _ => return, }; self.model_name = parts[..parts.len() - 1].join("-"); @@ -794,7 +797,7 @@ mod tests { ]); let config = ModelConfig::new("gpt-5.4-xhigh").unwrap(); assert_eq!(config.model_name, "gpt-5.4"); - assert_eq!(config.thinking_effort(), Some(ThinkingEffort::Max)); + assert_eq!(config.thinking_effort(), Some(ThinkingEffort::XHigh)); } #[test] @@ -857,7 +860,7 @@ mod tests { ); assert_eq!("med".parse::(), Ok(ThinkingEffort::Medium)); assert_eq!("max".parse::(), Ok(ThinkingEffort::Max)); - assert_eq!("xhigh".parse::(), Ok(ThinkingEffort::Max)); + assert_eq!("xhigh".parse::(), Ok(ThinkingEffort::XHigh)); assert!("invalid".parse::().is_err()); } } diff --git a/crates/goose/src/providers/chatgpt_codex.rs b/crates/goose/src/providers/chatgpt_codex.rs index fc341b2812..2fc83c73b4 100644 --- a/crates/goose/src/providers/chatgpt_codex.rs +++ b/crates/goose/src/providers/chatgpt_codex.rs @@ -243,6 +243,7 @@ fn reasoning_effort_for_config_with_config( ThinkingEffort::Low => &["low", "medium", "high", "xhigh"], ThinkingEffort::Medium => &["medium", "high", "low", "xhigh"], ThinkingEffort::High => &["high", "medium", "xhigh", "low"], + ThinkingEffort::XHigh => &["xhigh", "high", "medium", "low"], ThinkingEffort::Max => &["xhigh", "high", "medium", "low"], }; diff --git a/crates/goose/src/providers/codex.rs b/crates/goose/src/providers/codex.rs index 5811967e7c..0d2223f64c 100644 --- a/crates/goose/src/providers/codex.rs +++ b/crates/goose/src/providers/codex.rs @@ -69,7 +69,7 @@ impl CodexProvider { "low" => Some(crate::model::ThinkingEffort::Low), "medium" => Some(crate::model::ThinkingEffort::Medium), "high" => Some(crate::model::ThinkingEffort::High), - "xhigh" => Some(crate::model::ThinkingEffort::Max), + "xhigh" => Some(crate::model::ThinkingEffort::XHigh), _ => None, }) } @@ -84,6 +84,7 @@ impl CodexProvider { ThinkingEffort::Low => Some("low".to_string()), ThinkingEffort::Medium => Some("medium".to_string()), ThinkingEffort::High => Some("high".to_string()), + ThinkingEffort::XHigh => Some("xhigh".to_string()), ThinkingEffort::Max => Some("xhigh".to_string()), } } diff --git a/crates/goose/src/providers/formats/anthropic.rs b/crates/goose/src/providers/formats/anthropic.rs index 576a983742..65594cc712 100644 --- a/crates/goose/src/providers/formats/anthropic.rs +++ b/crates/goose/src/providers/formats/anthropic.rs @@ -507,7 +507,7 @@ pub fn adaptive_effort_value(model_config: &ModelConfig) -> Option<&'static str> } fn adaptive_effort_value_for_model( - model_name: &str, + _model_name: &str, effort: Option, ) -> Option<&'static str> { match effort? { @@ -515,7 +515,7 @@ fn adaptive_effort_value_for_model( ThinkingEffort::Low => Some("low"), ThinkingEffort::Medium => Some("medium"), ThinkingEffort::High => Some("high"), - ThinkingEffort::Max if is_claude_opus_47(model_name) => Some("xhigh"), + ThinkingEffort::XHigh => Some("xhigh"), ThinkingEffort::Max => Some("max"), } } @@ -569,6 +569,7 @@ fn thinking_budget_tokens_for_values( ThinkingEffort::Low => 4000, ThinkingEffort::Medium => 10000, ThinkingEffort::High => 16000, + ThinkingEffort::XHigh => 24000, ThinkingEffort::Max => 32000, }) } @@ -1281,6 +1282,23 @@ mod tests { let messages = vec![Message::user().with_text("Hello")]; let payload = create_request(&config, "system", &messages, &[])?; + assert_eq!(payload["thinking"]["type"], "adaptive"); + assert_eq!(payload["thinking"]["display"], "summarized"); + assert_eq!(payload["output_config"]["effort"], "max"); + assert_eq!(payload["max_tokens"], 4096); + + Ok(()) + } + + #[test] + fn test_create_request_adaptive_thinking_for_opus_47_xhigh_effort() -> Result<()> { + let _guard = env_lock::lock_env([("GOOSE_THINKING_EFFORT", None::<&str>)]); + + let mut config = cfg_with_effort("claude-opus-4-7", "xhigh"); + config.max_tokens = Some(4096); + let messages = vec![Message::user().with_text("Hello")]; + let payload = create_request(&config, "system", &messages, &[])?; + assert_eq!(payload["thinking"]["type"], "adaptive"); assert_eq!(payload["thinking"]["display"], "summarized"); assert_eq!(payload["output_config"]["effort"], "xhigh"); @@ -1470,7 +1488,7 @@ mod tests { assert_eq!(payload["thinking"]["type"], "adaptive"); assert_eq!(payload["thinking"]["display"], "summarized"); assert_eq!(payload["thinking"]["clear_thinking"], false); - assert_eq!(payload["output_config"]["effort"], "xhigh"); + assert_eq!(payload["output_config"]["effort"], "max"); assert!(payload["thinking"].get("budget_tokens").is_none()); assert_eq!(payload["max_tokens"], 4096); assert_eq!(payload["messages"][0]["content"][0]["type"], "thinking"); diff --git a/crates/goose/src/providers/formats/databricks.rs b/crates/goose/src/providers/formats/databricks.rs index 8ba3dd653e..ca982b9c41 100644 --- a/crates/goose/src/providers/formats/databricks.rs +++ b/crates/goose/src/providers/formats/databricks.rs @@ -1114,19 +1114,12 @@ mod tests { #[test] fn test_create_request_reasoning_effort_xhigh() -> anyhow::Result<()> { - let model_config = ModelConfig { - model_name: "o3-xhigh".to_string(), - context_limit: Some(4096), - temperature: None, - max_tokens: Some(1024), - toolshim: false, - toolshim_model: None, - fast_model_config: None, - request_params: None, - reasoning: None, - }; + let _guard = env_lock::lock_env([("GOOSE_THINKING_EFFORT", None::<&str>)]); + + let mut model_config = ModelConfig::new_or_fail("gpt-5.4-xhigh"); + model_config.max_tokens = Some(1024); let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?; - assert_eq!(request["model"], "o3"); + assert_eq!(request["model"], "gpt-5.4"); assert_eq!(request["reasoning_effort"], "xhigh"); Ok(()) } @@ -1209,10 +1202,17 @@ mod tests { #[test] fn test_create_request_enabled_thinking_budget_tracks_effort() -> anyhow::Result<()> { + let _guard = env_lock::lock_env([ + ("GOOSE_THINKING_EFFORT", None::<&str>), + ("ANTHROPIC_THINKING_BUDGET", None::<&str>), + ("CLAUDE_THINKING_BUDGET", None::<&str>), + ]); + for (effort, expected_budget) in [ ("low", 4000), ("medium", 10000), ("high", 16000), + ("xhigh", 24000), ("max", 32000), ] { let mut model_config = ModelConfig::new_or_fail("databricks-claude-3-7-sonnet"); diff --git a/crates/goose/src/providers/formats/google.rs b/crates/goose/src/providers/formats/google.rs index b35c2db504..b0de7afeba 100644 --- a/crates/goose/src/providers/formats/google.rs +++ b/crates/goose/src/providers/formats/google.rs @@ -553,7 +553,9 @@ fn get_thinking_config(model_config: &ModelConfig) -> Option { ThinkingEffort::Off | ThinkingEffort::Low | ThinkingEffort::Medium => { ThinkingLevel::Low } - ThinkingEffort::High | ThinkingEffort::Max => ThinkingLevel::High, + ThinkingEffort::High | ThinkingEffort::XHigh | ThinkingEffort::Max => { + ThinkingLevel::High + } }; Some(ThinkingConfig { diff --git a/crates/goose/src/providers/formats/openrouter.rs b/crates/goose/src/providers/formats/openrouter.rs index 22ac7465b2..d7d7ef35ba 100644 --- a/crates/goose/src/providers/formats/openrouter.rs +++ b/crates/goose/src/providers/formats/openrouter.rs @@ -94,6 +94,7 @@ fn reasoning_effort_for_openrouter(effort: ThinkingEffort) -> &'static str { ThinkingEffort::Low => "low", ThinkingEffort::Medium => "medium", ThinkingEffort::High => "high", + ThinkingEffort::XHigh => "xhigh", ThinkingEffort::Max => "xhigh", } } diff --git a/crates/goose/src/providers/utils.rs b/crates/goose/src/providers/utils.rs index 87be4af751..1c18d3a105 100644 --- a/crates/goose/src/providers/utils.rs +++ b/crates/goose/src/providers/utils.rs @@ -251,6 +251,7 @@ pub fn openai_reasoning_effort_for_thinking( ThinkingEffort::Low => &["low", "medium", "high", "xhigh"], ThinkingEffort::Medium => &["medium", "high", "low", "xhigh"], ThinkingEffort::High => &["high", "medium", "xhigh", "low"], + ThinkingEffort::XHigh => &["xhigh", "high", "medium", "low"], ThinkingEffort::Max => &["xhigh", "high", "medium", "low"], }; diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 7cebf3353e..90f267e145 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -8676,6 +8676,7 @@ "low", "medium", "high", + "xhigh", "max" ] }, diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index b5535a239f..10463baaef 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -1502,7 +1502,7 @@ export type ThinkingContent = { thinking: string; }; -export type ThinkingEffort = 'off' | 'low' | 'medium' | 'high' | 'max'; +export type ThinkingEffort = 'off' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'; export type TokenState = { accumulatedCost?: number | null; diff --git a/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx b/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx index a61a67aae7..0e831c010b 100644 --- a/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx +++ b/ui/desktop/src/components/settings/models/subcomponents/SwitchModelModal.tsx @@ -53,6 +53,10 @@ const i18n = defineMessages({ id: 'switchModelModal.claudeEffortHigh', defaultMessage: 'High - Deep reasoning (default)', }, + claudeEffortXHigh: { + id: 'switchModelModal.claudeEffortXHigh', + defaultMessage: 'XHigh - Extended coding and agentic work', + }, claudeEffortMax: { id: 'switchModelModal.claudeEffortMax', defaultMessage: 'Max - No constraints on thinking depth', @@ -262,6 +266,7 @@ export const SwitchModelModal = ({ { value: 'low', label: intl.formatMessage(i18n.claudeEffortLow) }, { value: 'medium', label: intl.formatMessage(i18n.claudeEffortMedium) }, { value: 'high', label: intl.formatMessage(i18n.claudeEffortHigh) }, + { value: 'xhigh', label: intl.formatMessage(i18n.claudeEffortXHigh) }, { value: 'max', label: intl.formatMessage(i18n.claudeEffortMax) }, ]; diff --git a/ui/desktop/src/i18n/messages/en.json b/ui/desktop/src/i18n/messages/en.json index 50f1406e49..d1554c7123 100644 --- a/ui/desktop/src/i18n/messages/en.json +++ b/ui/desktop/src/i18n/messages/en.json @@ -4409,6 +4409,9 @@ "switchModelModal.claudeEffortMedium": { "defaultMessage": "Medium - Moderate thinking" }, + "switchModelModal.claudeEffortXHigh": { + "defaultMessage": "XHigh - Extended coding and agentic work" + }, "switchModelModal.claudeEnabled": { "defaultMessage": "Enabled - Fixed token budget for thinking" }, diff --git a/ui/desktop/src/i18n/messages/zh-CN.json b/ui/desktop/src/i18n/messages/zh-CN.json index c3615acdd0..4361997e62 100644 --- a/ui/desktop/src/i18n/messages/zh-CN.json +++ b/ui/desktop/src/i18n/messages/zh-CN.json @@ -4307,6 +4307,9 @@ "switchModelModal.claudeEffortHigh": { "defaultMessage": "高 - 深度推理(默认)" }, + "switchModelModal.claudeEffortXHigh": { + "defaultMessage": "超高 - 长程编码和代理任务" + }, "switchModelModal.claudeEffortLow": { "defaultMessage": "低 - 最少思考,响应最快" },