#![deny(warnings)]
#![deny(clippy::all)]
pub mod fetch;
pub mod haiku;
pub mod search;
pub mod tools;
pub mod types;
use agentic_config::types::AnthropicServiceConfig;
use agentic_config::types::ExaServiceConfig;
use agentic_config::types::WebRetrievalConfig;
use tokio::sync::OnceCell;
pub struct WebTools {
pub(crate) http: reqwest::Client,
pub(crate) exa: exa_async::Client<exa_async::ExaConfig>,
pub(crate) anthropic: OnceCell<anthropic_async::Client<anthropic_async::AnthropicConfig>>,
pub(crate) cfg: WebRetrievalConfig,
pub(crate) anthropic_cfg: AnthropicServiceConfig,
}
impl WebTools {
#[must_use]
#[expect(
clippy::expect_used,
reason = "reqwest client build failure is rare (TLS/resolver init) and fatal; matches reqwest::Client::new() pattern"
)]
pub fn with_config(
cfg: WebRetrievalConfig,
exa_cfg: &ExaServiceConfig,
anthropic_cfg: AnthropicServiceConfig,
) -> Self {
let exa_config = exa_async::ExaConfig::new().with_api_base(&exa_cfg.base_url);
Self {
http: reqwest::Client::builder()
.connect_timeout(std::time::Duration::from_secs(5))
.timeout(std::time::Duration::from_secs(cfg.request_timeout_secs))
.build()
.expect("reqwest client"),
exa: exa_async::Client::with_config(exa_config),
anthropic: OnceCell::new(),
cfg,
anthropic_cfg,
}
}
#[must_use]
pub fn new() -> Self {
Self::with_config(
WebRetrievalConfig::default(),
&ExaServiceConfig::default(),
AnthropicServiceConfig::default(),
)
}
}
impl Default for WebTools {
fn default() -> Self {
Self::new()
}
}
pub use tools::build_registry;
#[cfg(test)]
impl WebTools {
pub(crate) fn with_http_client(http: reqwest::Client) -> Self {
let exa_cfg = ExaServiceConfig::default();
let exa_config = exa_async::ExaConfig::new().with_api_base(&exa_cfg.base_url);
Self {
http,
exa: exa_async::Client::with_config(exa_config),
anthropic: OnceCell::new(),
cfg: WebRetrievalConfig::default(),
anthropic_cfg: AnthropicServiceConfig::default(),
}
}
}