alith_tools/search/
mod.rs1use 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 #[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}