diff --git a/crates/anthropic/src/anthropic.rs b/crates/anthropic/src/anthropic.rs index ba79eb2315f96f52d7f695ae2e94c616763f94a0..db6f88d8b2ff5794ddbaaf8bf62c3b9dfababf7c 100644 --- a/crates/anthropic/src/anthropic.rs +++ b/crates/anthropic/src/anthropic.rs @@ -288,33 +288,43 @@ impl Model { } pub fn mode(&self) -> AnthropicModelMode { - if self.supports_adaptive_thinking() { - AnthropicModelMode::AdaptiveThinking - } else if self.supports_thinking() { - AnthropicModelMode::Thinking { + match self { + Self::Custom { mode, .. } => mode.clone(), + _ if self.supports_adaptive_thinking() => AnthropicModelMode::AdaptiveThinking, + _ if self.supports_thinking() => AnthropicModelMode::Thinking { budget_tokens: Some(4_096), - } - } else { - AnthropicModelMode::Default + }, + _ => AnthropicModelMode::Default, } } pub fn supports_thinking(&self) -> bool { - matches!( - self, - Self::ClaudeOpus4 - | Self::ClaudeOpus4_1 - | Self::ClaudeOpus4_5 - | Self::ClaudeOpus4_6 - | Self::ClaudeSonnet4 - | Self::ClaudeSonnet4_5 - | Self::ClaudeSonnet4_6 - | Self::ClaudeHaiku4_5 - ) + match self { + Self::Custom { mode, .. } => { + matches!( + mode, + AnthropicModelMode::Thinking { .. } | AnthropicModelMode::AdaptiveThinking + ) + } + _ => matches!( + self, + Self::ClaudeOpus4 + | Self::ClaudeOpus4_1 + | Self::ClaudeOpus4_5 + | Self::ClaudeOpus4_6 + | Self::ClaudeSonnet4 + | Self::ClaudeSonnet4_5 + | Self::ClaudeSonnet4_6 + | Self::ClaudeHaiku4_5 + ), + } } pub fn supports_adaptive_thinking(&self) -> bool { - matches!(self, Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6) + match self { + Self::Custom { mode, .. } => matches!(mode, AnthropicModelMode::AdaptiveThinking), + _ => matches!(self, Self::ClaudeOpus4_6 | Self::ClaudeSonnet4_6), + } } pub fn beta_headers(&self) -> Option { @@ -1110,6 +1120,65 @@ impl From for language_model_core::LanguageModelCompletionError { } } +#[test] +fn custom_mode_thinking_is_preserved() { + let model = Model::Custom { + name: "my-custom-model".to_string(), + max_tokens: 8192, + display_name: None, + tool_override: None, + cache_configuration: None, + max_output_tokens: None, + default_temperature: None, + extra_beta_headers: vec![], + mode: AnthropicModelMode::Thinking { + budget_tokens: Some(2048), + }, + }; + assert_eq!( + model.mode(), + AnthropicModelMode::Thinking { + budget_tokens: Some(2048) + } + ); + assert!(model.supports_thinking()); +} + +#[test] +fn custom_mode_adaptive_is_preserved() { + let model = Model::Custom { + name: "my-custom-model".to_string(), + max_tokens: 8192, + display_name: None, + tool_override: None, + cache_configuration: None, + max_output_tokens: None, + default_temperature: None, + extra_beta_headers: vec![], + mode: AnthropicModelMode::AdaptiveThinking, + }; + assert_eq!(model.mode(), AnthropicModelMode::AdaptiveThinking); + assert!(model.supports_adaptive_thinking()); + assert!(model.supports_thinking()); +} + +#[test] +fn custom_mode_default_disables_thinking() { + let model = Model::Custom { + name: "my-custom-model".to_string(), + max_tokens: 8192, + display_name: None, + tool_override: None, + cache_configuration: None, + max_output_tokens: None, + default_temperature: None, + extra_beta_headers: vec![], + mode: AnthropicModelMode::Default, + }; + assert!(!model.supports_thinking()); + assert!(!model.supports_adaptive_thinking()); +} + #[test] fn test_match_window_exceeded() { let error = ApiError {