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]]
name = "zed_llm_client"
version = "0.7.4"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2adf9bc80def4ec93c190f06eb78111865edc2576019a9753eaef6fd7bc3b72c"
checksum = "6fe0d60001c02d0d21a4114a13bee3a905fbb9e146ada80a90435c05fda18852"
dependencies = [
"anyhow",
"serde",

View File

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

View File

@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
use ui::{IconName, Tooltip, prelude::*};
use web_search::WebSearchRegistry;
use workspace::Workspace;
use zed_llm_client::{WebSearchCitation, WebSearchResponse};
use zed_llm_client::{WebSearchResponse, WebSearchResult};
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct WebSearchToolInput {
@ -120,10 +120,10 @@ impl ToolCard for WebSearchToolCard {
) -> impl IntoElement {
let header = match self.response.as_ref() {
Some(Ok(response)) => {
let text: SharedString = if response.citations.len() == 1 {
let text: SharedString = if response.results.len() == 1 {
"1 result".into()
} else {
format!("{} results", response.citations.len()).into()
format!("{} results", response.results.len()).into()
};
ToolCallCardHeader::new(IconName::Globe, "Searched the Web")
.with_secondary_text(text)
@ -134,52 +134,47 @@ impl ToolCard for WebSearchToolCard {
None => ToolCallCardHeader::new(IconName::Globe, "Searching the Web").loading(),
};
let content =
self.response.as_ref().and_then(|response| match response {
Ok(response) => {
Some(
v_flex()
.overflow_hidden()
.ml_1p5()
.pl(px(5.))
.border_l_1()
.border_color(cx.theme().colors().border_variant)
.gap_1()
.children(response.citations.iter().enumerate().map(
|(index, citation)| {
let title = citation.title.clone();
let url = citation.url.clone();
let content = self.response.as_ref().and_then(|response| match response {
Ok(response) => Some(
v_flex()
.overflow_hidden()
.ml_1p5()
.pl(px(5.))
.border_l_1()
.border_color(cx.theme().colors().border_variant)
.gap_1()
.children(response.results.iter().enumerate().map(|(index, result)| {
let title = result.title.clone();
let url = result.url.clone();
Button::new(("citation", index), title)
.label_size(LabelSize::Small)
.color(Color::Muted)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::XSmall)
.icon_position(IconPosition::End)
.truncate(true)
.tooltip({
let url = url.clone();
move |window, cx| {
Tooltip::with_meta(
"Citation Link",
None,
url.clone(),
window,
cx,
)
}
})
.on_click({
let url = url.clone();
move |_, _, cx| cx.open_url(&url)
})
},
))
.into_any(),
)
}
Err(_) => None,
});
Button::new(("result", index), title)
.label_size(LabelSize::Small)
.color(Color::Muted)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::XSmall)
.icon_position(IconPosition::End)
.truncate(true)
.tooltip({
let url = url.clone();
move |window, cx| {
Tooltip::with_meta(
"Web Search Result",
None,
url.clone(),
window,
cx,
)
}
})
.on_click({
let url = url.clone();
move |_, _, cx| cx.open_url(&url)
})
}))
.into_any(),
),
Err(_) => None,
});
v_flex().mb_3().gap_1().child(header).children(content)
}
@ -269,36 +264,39 @@ impl Component for WebSearchToolCard {
fn example_search_response() -> WebSearchResponse {
WebSearchResponse {
summary: r#"Toronto boasts a vibrant culinary scene with a diverse array of..."#
.to_string(),
citations: vec![
WebSearchCitation {
results: vec![
WebSearchResult {
title: "Alo".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(),
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(),
url: "https://www.google.com/maps/search/Sushi+Masaki+Saito%2C+Toronto%2C+Canada"
.to_string(),
range: Some(776..872),
text: "Information about Sushi Masaki Saito in Toronto.".to_string(),
},
WebSearchCitation {
WebSearchResult {
title: "Shoushin".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(),
url:
"https://www.google.com/maps/search/Restaurant+20+Victoria%2C+Toronto%2C+Canada"
.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 language_model::{LlmApiToken, RefreshLlmTokenListener};
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 {
state: Entity<State>,
@ -84,6 +86,7 @@ async fn perform_web_search(
let request = request_builder
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {token}"))
.header(CLIENT_SUPPORTS_EXA_WEB_SEARCH_PROVIDER_HEADER_NAME, "true")
.body(serde_json::to_string(&body)?.into())?;
let mut response = http_client
.send(request)