agent: Switch to new web search provider (#29951)

Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner 2025-05-06 00:47:11 +02:00 committed by Joseph T. Lyons
parent 94b335241c
commit a20c38463b
4 changed files with 66 additions and 65 deletions

4
Cargo.lock generated
View File

@ -18873,9 +18873,9 @@ dependencies = [
[[package]] [[package]]
name = "zed_llm_client" name = "zed_llm_client"
version = "0.7.4" version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2adf9bc80def4ec93c190f06eb78111865edc2576019a9753eaef6fd7bc3b72c" checksum = "6fe0d60001c02d0d21a4114a13bee3a905fbb9e146ada80a90435c05fda18852"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"serde", "serde",

View File

@ -609,7 +609,7 @@ wasmtime-wasi = "29"
which = "6.0.0" which = "6.0.0"
wit-component = "0.221" wit-component = "0.221"
workspace-hack = "0.1.0" workspace-hack = "0.1.0"
zed_llm_client = "0.7.4" zed_llm_client = "0.7.5"
zstd = "0.11" zstd = "0.11"
[workspace.dependencies.async-stripe] [workspace.dependencies.async-stripe]

View File

@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
use ui::{IconName, Tooltip, prelude::*}; use ui::{IconName, Tooltip, prelude::*};
use web_search::WebSearchRegistry; use web_search::WebSearchRegistry;
use workspace::Workspace; use workspace::Workspace;
use zed_llm_client::{WebSearchCitation, WebSearchResponse}; use zed_llm_client::{WebSearchResponse, WebSearchResult};
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct WebSearchToolInput { pub struct WebSearchToolInput {
@ -120,10 +120,10 @@ impl ToolCard for WebSearchToolCard {
) -> impl IntoElement { ) -> impl IntoElement {
let header = match self.response.as_ref() { let header = match self.response.as_ref() {
Some(Ok(response)) => { Some(Ok(response)) => {
let text: SharedString = if response.citations.len() == 1 { let text: SharedString = if response.results.len() == 1 {
"1 result".into() "1 result".into()
} else { } else {
format!("{} results", response.citations.len()).into() format!("{} results", response.results.len()).into()
}; };
ToolCallCardHeader::new(IconName::Globe, "Searched the Web") ToolCallCardHeader::new(IconName::Globe, "Searched the Web")
.with_secondary_text(text) .with_secondary_text(text)
@ -134,10 +134,8 @@ impl ToolCard for WebSearchToolCard {
None => ToolCallCardHeader::new(IconName::Globe, "Searching the Web").loading(), None => ToolCallCardHeader::new(IconName::Globe, "Searching the Web").loading(),
}; };
let content = let content = self.response.as_ref().and_then(|response| match response {
self.response.as_ref().and_then(|response| match response { Ok(response) => Some(
Ok(response) => {
Some(
v_flex() v_flex()
.overflow_hidden() .overflow_hidden()
.ml_1p5() .ml_1p5()
@ -145,12 +143,11 @@ impl ToolCard for WebSearchToolCard {
.border_l_1() .border_l_1()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border_variant)
.gap_1() .gap_1()
.children(response.citations.iter().enumerate().map( .children(response.results.iter().enumerate().map(|(index, result)| {
|(index, citation)| { let title = result.title.clone();
let title = citation.title.clone(); let url = result.url.clone();
let url = citation.url.clone();
Button::new(("citation", index), title) Button::new(("result", index), title)
.label_size(LabelSize::Small) .label_size(LabelSize::Small)
.color(Color::Muted) .color(Color::Muted)
.icon(IconName::ArrowUpRight) .icon(IconName::ArrowUpRight)
@ -161,7 +158,7 @@ impl ToolCard for WebSearchToolCard {
let url = url.clone(); let url = url.clone();
move |window, cx| { move |window, cx| {
Tooltip::with_meta( Tooltip::with_meta(
"Citation Link", "Web Search Result",
None, None,
url.clone(), url.clone(),
window, window,
@ -173,11 +170,9 @@ impl ToolCard for WebSearchToolCard {
let url = url.clone(); let url = url.clone();
move |_, _, cx| cx.open_url(&url) move |_, _, cx| cx.open_url(&url)
}) })
}, }))
))
.into_any(), .into_any(),
) ),
}
Err(_) => None, Err(_) => None,
}); });
@ -269,36 +264,39 @@ impl Component for WebSearchToolCard {
fn example_search_response() -> WebSearchResponse { fn example_search_response() -> WebSearchResponse {
WebSearchResponse { WebSearchResponse {
summary: r#"Toronto boasts a vibrant culinary scene with a diverse array of..."# results: vec![
.to_string(), WebSearchResult {
citations: vec![
WebSearchCitation {
title: "Alo".to_string(), title: "Alo".to_string(),
url: "https://www.google.com/maps/search/Alo%2C+Toronto%2C+Canada".to_string(), url: "https://www.google.com/maps/search/Alo%2C+Toronto%2C+Canada".to_string(),
range: Some(147..213), text: "Alo is a popular restaurant in Toronto.".to_string(),
}, },
WebSearchCitation { WebSearchResult {
title: "Alo".to_string(),
url: "https://www.google.com/maps/search/Alo%2C+Toronto%2C+Canada".to_string(),
text: "Information about Alo restaurant in Toronto.".to_string(),
},
WebSearchResult {
title: "Edulis".to_string(), title: "Edulis".to_string(),
url: "https://www.google.com/maps/search/Edulis%2C+Toronto%2C+Canada".to_string(), url: "https://www.google.com/maps/search/Edulis%2C+Toronto%2C+Canada".to_string(),
range: Some(447..519), text: "Details about Edulis restaurant in Toronto.".to_string(),
}, },
WebSearchCitation { WebSearchResult {
title: "Sushi Masaki Saito".to_string(), title: "Sushi Masaki Saito".to_string(),
url: "https://www.google.com/maps/search/Sushi+Masaki+Saito%2C+Toronto%2C+Canada" url: "https://www.google.com/maps/search/Sushi+Masaki+Saito%2C+Toronto%2C+Canada"
.to_string(), .to_string(),
range: Some(776..872), text: "Information about Sushi Masaki Saito in Toronto.".to_string(),
}, },
WebSearchCitation { WebSearchResult {
title: "Shoushin".to_string(), title: "Shoushin".to_string(),
url: "https://www.google.com/maps/search/Shoushin%2C+Toronto%2C+Canada".to_string(), url: "https://www.google.com/maps/search/Shoushin%2C+Toronto%2C+Canada".to_string(),
range: Some(1072..1148), text: "Details about Shoushin restaurant in Toronto.".to_string(),
}, },
WebSearchCitation { WebSearchResult {
title: "Restaurant 20 Victoria".to_string(), title: "Restaurant 20 Victoria".to_string(),
url: url:
"https://www.google.com/maps/search/Restaurant+20+Victoria%2C+Toronto%2C+Canada" "https://www.google.com/maps/search/Restaurant+20+Victoria%2C+Toronto%2C+Canada"
.to_string(), .to_string(),
range: Some(1291..1395), text: "Information about Restaurant 20 Victoria in Toronto.".to_string(),
}, },
], ],
} }

View File

@ -7,7 +7,9 @@ use gpui::{App, AppContext, Context, Entity, Subscription, Task};
use http_client::{HttpClient, Method}; use http_client::{HttpClient, Method};
use language_model::{LlmApiToken, RefreshLlmTokenListener}; use language_model::{LlmApiToken, RefreshLlmTokenListener};
use web_search::{WebSearchProvider, WebSearchProviderId}; use web_search::{WebSearchProvider, WebSearchProviderId};
use zed_llm_client::{WebSearchBody, WebSearchResponse}; use zed_llm_client::{
CLIENT_SUPPORTS_EXA_WEB_SEARCH_PROVIDER_HEADER_NAME, WebSearchBody, WebSearchResponse,
};
pub struct CloudWebSearchProvider { pub struct CloudWebSearchProvider {
state: Entity<State>, state: Entity<State>,
@ -84,6 +86,7 @@ async fn perform_web_search(
let request = request_builder let request = request_builder
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {token}")) .header("Authorization", format!("Bearer {token}"))
.header(CLIENT_SUPPORTS_EXA_WEB_SEARCH_PROVIDER_HEADER_NAME, "true")
.body(serde_json::to_string(&body)?.into())?; .body(serde_json::to_string(&body)?.into())?;
let mut response = http_client let mut response = http_client
.send(request) .send(request)