tool_compatibility.rs

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