Skip to main content

alith_tools/search/
mod.rs

1use std::sync::Arc;
2
3use alith_core::tool::{StructureTool, Tool, ToolError};
4use async_trait::async_trait;
5use serde::{Deserialize, Serialize};
6
7pub mod duckduckgo;
8
9#[derive(Debug, Default)]
10pub enum SearchProvider {
11    #[default]
12    DuckDuckGo,
13}
14
15#[derive(Debug, thiserror::Error)]
16#[error("Search error")]
17pub enum SearchError {
18    #[error("Failed to search: {0}")]
19    SearchError(String),
20    #[error("An unknown error occurred: {0}")]
21    Unknown(String),
22    #[error("IO error: {0}")]
23    IoError(#[from] std::io::Error),
24    #[error("Url error: {0}")]
25    UrlError(#[from] url::ParseError),
26    #[error("Request error: {0}")]
27    RequestError(#[from] reqwest::Error),
28    /// JSON error (e.g.: serialization, deserialization, etc.)
29    #[error("JSON error: {0}")]
30    JsonError(#[from] serde_json::Error),
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SearchResult {
35    title: String,
36    link: String,
37    snippet: String,
38}
39
40pub type SearchResults = Vec<SearchResult>;
41
42#[async_trait]
43pub trait Search: Tool {
44    async fn search(&self, query: &str) -> Result<SearchResults, SearchError>;
45}
46
47pub struct SearchTool {
48    provider: SearchProvider,
49    searcher: Arc<dyn Search>,
50}
51
52impl Default for SearchTool {
53    fn default() -> Self {
54        let provider = SearchProvider::default();
55        Self {
56            searcher: Self::searcher(&provider),
57            provider,
58        }
59    }
60}
61
62impl SearchTool {
63    #[inline]
64    pub fn provider(&self) -> &SearchProvider {
65        &self.provider
66    }
67
68    #[inline]
69    pub fn searcher(provider: &SearchProvider) -> Arc<dyn Search> {
70        Arc::new(match provider {
71            SearchProvider::DuckDuckGo => duckduckgo::Searcher::default(),
72        })
73    }
74}
75
76#[async_trait]
77impl StructureTool for SearchTool {
78    type Input = String;
79    type Output = SearchResults;
80
81    #[inline]
82    fn name(&self) -> &str {
83        self.searcher.name()
84    }
85
86    #[inline]
87    fn description(&self) -> &str {
88        self.searcher.description()
89    }
90
91    #[inline]
92    fn version(&self) -> &str {
93        self.searcher.version()
94    }
95
96    #[inline]
97    fn author(&self) -> &str {
98        self.searcher.version()
99    }
100
101    #[inline]
102    async fn run_with_args(&self, input: Self::Input) -> Result<Self::Output, ToolError> {
103        self.searcher
104            .search(&input)
105            .await
106            .map_err(|err| ToolError::NormalError(Box::new(err)))
107    }
108}