tool_compatibility.rs

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