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