diff --git a/Cargo.lock b/Cargo.lock index b35f0c76f4ea15132c75ee582ffab601c81f83c1..04d2c930827e01cc30a4876d9fa6cacd9d67438b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -705,6 +705,7 @@ dependencies = [ "assistant_tool", "chrono", "collections", + "component", "feature_flags", "futures 0.3.31", "gpui", @@ -714,6 +715,7 @@ dependencies = [ "itertools 0.14.0", "language", "language_model", + "linkme", "open", "pretty_assertions", "project", diff --git a/crates/assistant_tools/Cargo.toml b/crates/assistant_tools/Cargo.toml index 1086298292165998cdc51cdb0b68e2a96e0d5e1e..eaaeff1e47c854f346c87b85e79a71df9579e4b4 100644 --- a/crates/assistant_tools/Cargo.toml +++ b/crates/assistant_tools/Cargo.toml @@ -16,6 +16,7 @@ anyhow.workspace = true assistant_tool.workspace = true chrono.workspace = true collections.workspace = true +component.workspace = true feature_flags.workspace = true futures.workspace = true gpui.workspace = true @@ -25,6 +26,8 @@ indoc.workspace = true itertools.workspace = true language.workspace = true language_model.workspace = true +linkme.workspace = true +open.workspace = true project.workspace = true regex.workspace = true schemars.workspace = true @@ -32,10 +35,9 @@ serde.workspace = true serde_json.workspace = true ui.workspace = true util.workspace = true -worktree.workspace = true -open = { workspace = true } web_search.workspace = true workspace-hack.workspace = true +worktree.workspace = true zed_llm_client.workspace = true [dev-dependencies] diff --git a/crates/assistant_tools/src/web_search_tool.rs b/crates/assistant_tools/src/web_search_tool.rs index b746a3e2944ec2098164afdf9159a48b74797aed..b64b9a78c776376e8af0f425c749b1e23cff8b42 100644 --- a/crates/assistant_tools/src/web_search_tool.rs +++ b/crates/assistant_tools/src/web_search_tool.rs @@ -12,7 +12,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use ui::{IconName, Tooltip, prelude::*}; use web_search::WebSearchRegistry; -use zed_llm_client::WebSearchResponse; +use zed_llm_client::{WebSearchCitation, WebSearchResponse}; #[derive(Debug, Serialize, Deserialize, JsonSchema)] pub struct WebSearchToolInput { @@ -20,6 +20,7 @@ pub struct WebSearchToolInput { query: String, } +#[derive(RegisterComponent)] pub struct WebSearchTool; impl Tool for WebSearchTool { @@ -178,3 +179,111 @@ impl ToolCard for WebSearchToolCard { v_flex().mb_3().gap_1().child(header).children(content) } } + +impl Component for WebSearchTool { + fn scope() -> ComponentScope { + ComponentScope::Agent + } + + fn sort_name() -> &'static str { + "ToolWebSearch" + } + + fn preview(window: &mut Window, cx: &mut App) -> Option { + let in_progress_search = cx.new(|cx| WebSearchToolCard { + response: None, + _task: cx.spawn(async move |_this, cx| { + loop { + cx.background_executor() + .timer(Duration::from_secs(60)) + .await + } + }), + }); + + let successful_search = cx.new(|_cx| WebSearchToolCard { + response: Some(Ok(example_search_response())), + _task: Task::ready(()), + }); + + let error_search = cx.new(|_cx| WebSearchToolCard { + response: Some(Err(anyhow!("Failed to resolve https://google.com"))), + _task: Task::ready(()), + }); + + Some( + v_flex() + .gap_6() + .children(vec![example_group(vec![ + single_example( + "In Progress", + div() + .size_full() + .child(in_progress_search.update(cx, |tool, cx| { + tool.render(&ToolUseStatus::Pending, window, cx) + .into_any_element() + })) + .into_any_element(), + ), + single_example( + "Successful", + div() + .size_full() + .child(successful_search.update(cx, |tool, cx| { + tool.render(&ToolUseStatus::Finished("".into()), window, cx) + .into_any_element() + })) + .into_any_element(), + ), + single_example( + "Error", + div() + .size_full() + .child(error_search.update(cx, |tool, cx| { + tool.render(&ToolUseStatus::Error("".into()), window, cx) + .into_any_element() + })) + .into_any_element(), + ), + ])]) + .into_any_element(), + ) + } +} + +fn example_search_response() -> WebSearchResponse { + WebSearchResponse { + summary: r#"Toronto boasts a vibrant culinary scene with a diverse array of..."# + .to_string(), + citations: vec![ + WebSearchCitation { + title: "Alo".to_string(), + url: "https://www.google.com/maps/search/Alo%2C+Toronto%2C+Canada".to_string(), + range: Some(147..213), + }, + WebSearchCitation { + title: "Edulis".to_string(), + url: "https://www.google.com/maps/search/Edulis%2C+Toronto%2C+Canada".to_string(), + range: Some(447..519), + }, + WebSearchCitation { + title: "Sushi Masaki Saito".to_string(), + url: "https://www.google.com/maps/search/Sushi+Masaki+Saito%2C+Toronto%2C+Canada" + .to_string(), + range: Some(776..872), + }, + WebSearchCitation { + title: "Shoushin".to_string(), + url: "https://www.google.com/maps/search/Shoushin%2C+Toronto%2C+Canada".to_string(), + range: Some(1072..1148), + }, + WebSearchCitation { + title: "Restaurant 20 Victoria".to_string(), + url: + "https://www.google.com/maps/search/Restaurant+20+Victoria%2C+Toronto%2C+Canada" + .to_string(), + range: Some(1291..1395), + }, + ], + } +} diff --git a/crates/component/src/component.rs b/crates/component/src/component.rs index db847d553876a3ebf442ba4ee6ca07497c573572..8394c91b9848a50483519cb56f8c60bce689a8e0 100644 --- a/crates/component/src/component.rs +++ b/crates/component/src/component.rs @@ -201,6 +201,7 @@ pub fn components() -> AllComponents { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ComponentScope { + Agent, Collaboration, DataDisplay, Editor, @@ -220,6 +221,7 @@ pub enum ComponentScope { impl Display for ComponentScope { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + ComponentScope::Agent => write!(f, "Agent"), ComponentScope::Collaboration => write!(f, "Collaboration"), ComponentScope::DataDisplay => write!(f, "Data Display"), ComponentScope::Editor => write!(f, "Editor"), diff --git a/typos.toml b/typos.toml index 2622e31c7f4b8b4a02b8a3372550a4c9aa10d751..4952c61c6ac20227d9be46f8b7b020cbd89667ee 100644 --- a/typos.toml +++ b/typos.toml @@ -19,6 +19,9 @@ extend-exclude = [ # Some crate names are flagged as typos. "crates/indexed_docs/src/providers/rustdoc/popular_crates.txt", + # Some mock data is flagged as typos. + "crates/assistant_tools/src/web_search_tool.rs", + # Stripe IDs are flagged as typos. "crates/collab/src/db/tests/processed_stripe_event_tests.rs", # Not our typos.