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}