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 = cx.subscribe(&thread, |this, _, event, _| {
18            if let ThreadEvent::ProfileChanged = event {
19                this.cache.clear();
20            }
21        });
22
23        Self {
24            cache: HashMap::default(),
25            thread,
26            _thread_subscription: _tool_working_set_subscription,
27        }
28    }
29
30    pub fn incompatible_tools(
31        &mut self,
32        model: &Arc<dyn LanguageModel>,
33        cx: &App,
34    ) -> &[Arc<dyn Tool>] {
35        self.cache
36            .entry(model.tool_input_format())
37            .or_insert_with(|| {
38                self.thread
39                    .read(cx)
40                    .profile()
41                    .enabled_tools(cx)
42                    .iter()
43                    .filter(|(_, tool)| tool.input_schema(model.tool_input_format()).is_err())
44                    .map(|(_, tool)| tool.clone())
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}