use llmsdk_provider::language_model::{ProviderTool, Tool};
use serde::Serialize;
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WebSearchOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub allowed_domains: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub excluded_domains: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_image_understanding: Option<bool>,
}
#[must_use]
pub fn web_search(opts: &WebSearchOptions) -> Tool {
let args = serde_json::to_value(opts)
.ok()
.and_then(|v| v.as_object().cloned());
Tool::Provider(ProviderTool {
id: "xai.web_search".into(),
name: "web_search".into(),
args,
provider_options: None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_args_serialize_to_empty_object() {
let Tool::Provider(p) = web_search(&WebSearchOptions::default()) else {
panic!("expected provider tool");
};
assert!(p.args.is_some_and(|m| m.is_empty()));
}
#[test]
fn allowed_domains_serialize_with_snake_case_wire_key() {
let Tool::Provider(p) = web_search(&WebSearchOptions {
allowed_domains: Some(vec!["a.com".into()]),
..Default::default()
}) else {
panic!("expected provider tool");
};
let args = p.args.unwrap();
assert_eq!(args["allowedDomains"][0], "a.com");
}
}