tool_compatibility.rs

 1use std::sync::Arc;
 2
 3use assistant_tool::{Tool, ToolSource};
 4use collections::HashMap;
 5use gpui::{App, Context, Entity, IntoElement, Render, Subscription, Window};
 6use language_model::{LanguageModel, LanguageModelToolSchemaFormat};
 7use ui::prelude::*;
 8
 9use crate::{Thread, ThreadEvent};
10
11pub struct IncompatibleToolsState {
12    cache: HashMap<LanguageModelToolSchemaFormat, Vec<Arc<dyn Tool>>>,
13    thread: Entity<Thread>,
14    _thread_subscription: Subscription,
15}
16
17impl IncompatibleToolsState {
18    pub fn new(thread: Entity<Thread>, cx: &mut Context<Self>) -> Self {
19        let _tool_working_set_subscription =
20            cx.subscribe(&thread, |this, _, event, _| match event {
21                ThreadEvent::ProfileChanged => {
22                    this.cache.clear();
23                }
24                _ => {}
25            });
26
27        Self {
28            cache: HashMap::default(),
29            thread,
30            _thread_subscription: _tool_working_set_subscription,
31        }
32    }
33
34    pub fn incompatible_tools(
35        &mut self,
36        model: &Arc<dyn LanguageModel>,
37        cx: &App,
38    ) -> &[Arc<dyn Tool>] {
39        self.cache
40            .entry(model.tool_input_format())
41            .or_insert_with(|| {
42                self.thread
43                    .read(cx)
44                    .profile()
45                    .enabled_tools(cx)
46                    .iter()
47                    .filter(|tool| tool.input_schema(model.tool_input_format()).is_err())
48                    .cloned()
49                    .collect()
50            })
51    }
52}
53
54pub struct IncompatibleToolsTooltip {
55    pub incompatible_tools: Vec<Arc<dyn Tool>>,
56}
57
58impl Render for IncompatibleToolsTooltip {
59    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
60        ui::tooltip_container(window, cx, |container, _, cx| {
61            container
62                .w_72()
63                .child(Label::new("Incompatible Tools").size(LabelSize::Small))
64                .child(
65                    Label::new(
66                        "This model is incompatible with the following tools from your MCPs:",
67                    )
68                    .size(LabelSize::Small)
69                    .color(Color::Muted),
70                )
71                .child(
72                    v_flex()
73                        .my_1p5()
74                        .py_0p5()
75                        .border_b_1()
76                        .border_color(cx.theme().colors().border_variant)
77                        .children(
78                            self.incompatible_tools
79                                .iter()
80                                .map(|tool| h_flex().gap_4().child(Label::new(tool.name()).size(LabelSize::Small)).map(|parent|
81                                    match tool.source() {
82                                        ToolSource::Native => parent,
83                                        ToolSource::ContextServer { id } => parent.child(Label::new(id).size(LabelSize::Small).color(Color::Muted)),
84                                    }
85                                )),
86                        ),
87                )
88                .child(Label::new("What To Do Instead").size(LabelSize::Small))
89                .child(
90                    Label::new(
91                        "Every other tool continues to work with this model, but to specifically use those, switch to another model.",
92                    )
93                    .size(LabelSize::Small)
94                    .color(Color::Muted),
95                )
96        })
97    }
98}