tool_compatibility.rs

 1use agent::{Thread, ThreadEvent};
 2use assistant_tool::{AnyTool, 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<AnyTool>>,
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(&mut self, model: &Arc<dyn LanguageModel>, cx: &App) -> &[AnyTool] {
33        self.cache
34            .entry(model.tool_input_format())
35            .or_insert_with(|| {
36                self.thread
37                    .read(cx)
38                    .profile()
39                    .enabled_tools(cx)
40                    .iter()
41                    .filter(|(_, tool)| tool.input_schema(model.tool_input_format()).is_err())
42                    .map(|(_, tool)| tool.clone())
43                    .collect()
44            })
45    }
46}
47
48pub struct IncompatibleToolsTooltip {
49    pub incompatible_tools: Vec<AnyTool>,
50}
51
52impl Render for IncompatibleToolsTooltip {
53    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
54        ui::tooltip_container(window, cx, |container, _, cx| {
55            container
56                .w_72()
57                .child(Label::new("Incompatible Tools").size(LabelSize::Small))
58                .child(
59                    Label::new(
60                        "This model is incompatible with the following tools from your MCPs:",
61                    )
62                    .size(LabelSize::Small)
63                    .color(Color::Muted),
64                )
65                .child(
66                    v_flex()
67                        .my_1p5()
68                        .py_0p5()
69                        .border_b_1()
70                        .border_color(cx.theme().colors().border_variant)
71                        .children(
72                            self.incompatible_tools
73                                .iter()
74                                .map(|tool| h_flex().gap_4().child(Label::new(tool.name()).size(LabelSize::Small)).map(|parent|
75                                    match tool.source() {
76                                        ToolSource::Native => parent,
77                                        ToolSource::ContextServer { id } => parent.child(Label::new(id).size(LabelSize::Small).color(Color::Muted)),
78                                    }
79                                )),
80                        ),
81                )
82                .child(Label::new("What To Do Instead").size(LabelSize::Small))
83                .child(
84                    Label::new(
85                        "Every other tool continues to work with this model, but to specifically use those, switch to another model.",
86                    )
87                    .size(LabelSize::Small)
88                    .color(Color::Muted),
89                )
90        })
91    }
92}