assistant2: Add affordances for when the selected model does not support tools (#27870)

Marshall Bowers created

This PR adds some affordances for when the currently-selected model does
not support tools.

We disable the profile selector and put it into a "No Tools" state:

<img width="1394" alt="Screenshot 2025-04-01 at 3 58 00 PM"
src="https://github.com/user-attachments/assets/de6ecb0f-7657-4e16-9d5d-7bbfbc2b0a5c"
/>

We will also only attach tools to the request to the model if the model
supports it.

Release Notes:

- N/A

Change summary

crates/assistant2/src/profile_selector.rs | 25 +++++++++++++++++++++----
crates/assistant2/src/thread.rs           | 24 +++++++++++++-----------
2 files changed, 34 insertions(+), 15 deletions(-)

Detailed changes

crates/assistant2/src/profile_selector.rs 🔗

@@ -4,9 +4,10 @@ use assistant_settings::{AgentProfile, AssistantSettings};
 use fs::Fs;
 use gpui::{Action, Entity, FocusHandle, Subscription, WeakEntity, prelude::*};
 use indexmap::IndexMap;
+use language_model::LanguageModelRegistry;
 use settings::{Settings as _, SettingsStore, update_settings_file};
 use ui::{
-    ButtonLike, ContextMenu, ContextMenuEntry, KeyBinding, PopoverMenu, PopoverMenuHandle,
+    ButtonLike, ContextMenu, ContextMenuEntry, KeyBinding, PopoverMenu, PopoverMenuHandle, Tooltip,
     prelude::*,
 };
 use util::ResultExt as _;
@@ -127,6 +128,11 @@ impl Render for ProfileSelector {
             .map(|profile| profile.name.clone())
             .unwrap_or_else(|| "Unknown".into());
 
+        let model_registry = LanguageModelRegistry::read_global(cx);
+        let supports_tools = model_registry
+            .active_model()
+            .map_or(false, |model| model.supports_tools());
+
         let icon = match profile_id.as_ref() {
             "write" => IconName::Pencil,
             "ask" => IconName::MessageBubbles,
@@ -139,7 +145,7 @@ impl Render for ProfileSelector {
             .menu(move |window, cx| {
                 Some(this.update(cx, |this, cx| this.build_context_menu(window, cx)))
             })
-            .trigger(
+            .trigger(if supports_tools {
                 ButtonLike::new("profile-selector-button").child(
                     h_flex()
                         .gap_1()
@@ -164,8 +170,19 @@ impl Render for ProfileSelector {
                             )
                             .map(|kb| kb.size(rems_from_px(10.)))
                         })),
-                ),
-            )
+                )
+            } else {
+                ButtonLike::new("tools-not-supported-button")
+                    .disabled(true)
+                    .child(
+                        h_flex().gap_1().child(
+                            Label::new("No Tools")
+                                .size(LabelSize::Small)
+                                .color(Color::Muted),
+                        ),
+                    )
+                    .tooltip(Tooltip::text("The current model does not support tools."))
+            })
             .anchor(gpui::Corner::BottomLeft)
             .with_handle(self.menu_handle.clone())
     }

crates/assistant2/src/thread.rs 🔗

@@ -780,18 +780,20 @@ impl Thread {
         cx: &mut Context<Self>,
     ) {
         let mut request = self.to_completion_request(request_kind, cx);
-        request.tools = {
-            let mut tools = Vec::new();
-            tools.extend(self.tools().enabled_tools(cx).into_iter().map(|tool| {
-                LanguageModelRequestTool {
-                    name: tool.name(),
-                    description: tool.description(),
-                    input_schema: tool.input_schema(model.tool_input_format()),
-                }
-            }));
+        if model.supports_tools() {
+            request.tools = {
+                let mut tools = Vec::new();
+                tools.extend(self.tools().enabled_tools(cx).into_iter().map(|tool| {
+                    LanguageModelRequestTool {
+                        name: tool.name(),
+                        description: tool.description(),
+                        input_schema: tool.input_schema(model.tool_input_format()),
+                    }
+                }));
 
-            tools
-        };
+                tools
+            };
+        }
 
         self.stream_completion(request, model, cx);
     }