Fix thinking models when using non-zed providers (#49117)

Bennet Bo Fenner created

This PR #47407 broke thinking models for non-zed providers (Nightly
only) because we set `thinking_allowed` to `thinking_enabled`
(previously `true`)
[here](https://github.com/zed-industries/zed/blob/b671c8cdc5a73d49d158a84f386f8fecbfbae65a/crates/agent/src/thread.rs#L2400).
The value of `thinking_enabled` eventually comes from the
`LanguageModel::supports_thinking` function, which returns `false` for
all models except the ones coming from Zed.
This PR implements `thinking_enabled` for other providers.

Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- N/A

Change summary

crates/agent_ui/src/agent_panel.rs                 | 8 ++++++--
crates/language_models/src/provider/anthropic.rs   | 4 ++++
crates/language_models/src/provider/bedrock.rs     | 7 +++++++
crates/language_models/src/provider/google.rs      | 4 ++++
crates/language_models/src/provider/ollama.rs      | 4 ++++
crates/language_models/src/provider/open_ai.rs     | 4 ++++
crates/language_models/src/provider/open_router.rs | 4 ++++
7 files changed, 33 insertions(+), 2 deletions(-)

Detailed changes

crates/agent_ui/src/agent_panel.rs 🔗

@@ -1496,6 +1496,10 @@ impl AgentPanel {
                 {
                     update_settings_file(self.fs.clone(), cx, move |settings, _| {
                         let provider = model.provider_id().0.to_string();
+                        let enable_thinking = model.supports_thinking();
+                        let effort = model
+                            .default_effort_level()
+                            .map(|effort| effort.value.to_string());
                         let model = model.id().0.to_string();
                         settings
                             .agent
@@ -1503,8 +1507,8 @@ impl AgentPanel {
                             .set_model(LanguageModelSelection {
                                 provider: LanguageModelProviderSetting(provider),
                                 model,
-                                enable_thinking: false,
-                                effort: None,
+                                enable_thinking,
+                                effort,
                             })
                     });
                 }

crates/language_models/src/provider/anthropic.rs 🔗

@@ -515,6 +515,10 @@ impl LanguageModel for AnthropicModel {
         }
     }
 
+    fn supports_thinking(&self) -> bool {
+        matches!(self.model.mode(), AnthropicModelMode::Thinking { .. })
+    }
+
     fn telemetry_id(&self) -> String {
         format!("anthropic/{}", self.model.id())
     }

crates/language_models/src/provider/bedrock.rs 🔗

@@ -640,6 +640,13 @@ impl LanguageModel for BedrockModel {
         self.model.supports_images()
     }
 
+    fn supports_thinking(&self) -> bool {
+        matches!(
+            self.model.mode(),
+            BedrockModelMode::Thinking { .. } | BedrockModelMode::AdaptiveThinking { .. }
+        )
+    }
+
     fn supports_tool_choice(&self, choice: LanguageModelToolChoice) -> bool {
         match choice {
             LanguageModelToolChoice::Auto | LanguageModelToolChoice::Any => {

crates/language_models/src/provider/google.rs 🔗

@@ -283,6 +283,10 @@ impl LanguageModel for GoogleLanguageModel {
         self.model.supports_images()
     }
 
+    fn supports_thinking(&self) -> bool {
+        matches!(self.model.mode(), GoogleModelMode::Thinking { .. })
+    }
+
     fn supports_tool_choice(&self, choice: LanguageModelToolChoice) -> bool {
         match choice {
             LanguageModelToolChoice::Auto

crates/language_models/src/provider/ollama.rs 🔗

@@ -443,6 +443,10 @@ impl LanguageModel for OllamaLanguageModel {
         self.model.supports_vision.unwrap_or(false)
     }
 
+    fn supports_thinking(&self) -> bool {
+        self.model.supports_thinking.unwrap_or(false)
+    }
+
     fn supports_tool_choice(&self, choice: LanguageModelToolChoice) -> bool {
         match choice {
             LanguageModelToolChoice::Auto => false,

crates/language_models/src/provider/open_ai.rs 🔗

@@ -327,6 +327,10 @@ impl LanguageModel for OpenAiLanguageModel {
         }
     }
 
+    fn supports_thinking(&self) -> bool {
+        self.model.reasoning_effort().is_some()
+    }
+
     fn supports_split_token_display(&self) -> bool {
         true
     }

crates/language_models/src/provider/open_router.rs 🔗

@@ -314,6 +314,10 @@ impl LanguageModel for OpenRouterLanguageModel {
         self.model.supports_tool_calls()
     }
 
+    fn supports_thinking(&self) -> bool {
+        matches!(self.model.mode, OpenRouterModelMode::Thinking { .. })
+    }
+
     fn tool_input_format(&self) -> LanguageModelToolSchemaFormat {
         let model_id = self.model.id().trim().to_lowercase();
         if model_id.contains("gemini") || model_id.contains("grok") {