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}