1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
5#[serde(rename_all = "lowercase")]
6pub enum SearchProviderType {
7 Brave,
8 Exa,
9 Serper,
10 SerpApi,
11 DuckDuckGo,
12 Jina,
13 Tavily,
14}
15
16#[derive(Debug, Serialize, Deserialize, Clone)]
17pub struct SearchProviderConfig {
18 pub url: String,
19 pub provider_type: SearchProviderType,
20 #[serde(default)]
21 pub headers: HashMap<String, String>,
22}
23
24impl SearchProviderConfig {
25 #[allow(dead_code)]
26 pub fn new(url: String, provider_type: SearchProviderType) -> Self {
27 Self {
28 url,
29 provider_type,
30 headers: HashMap::new(),
31 }
32 }
33}
34
35impl SearchProviderType {
36 pub fn detect_from_url(url: &str) -> anyhow::Result<Self> {
38 let url_lower = url.to_lowercase();
39
40 if url_lower.contains("api.search.brave.com") {
41 Ok(SearchProviderType::Brave)
42 } else if url_lower.contains("api.exa.ai") || url_lower.contains("exa.ai") {
43 Ok(SearchProviderType::Exa)
44 } else if url_lower.contains("google.serper.dev") || url_lower.contains("serper.dev") {
45 Ok(SearchProviderType::Serper)
46 } else if url_lower.contains("serpapi.com") {
47 Ok(SearchProviderType::SerpApi)
48 } else if url_lower.contains("duckduckgo.com") || url_lower.contains("api.duckduckgo.com") {
49 Ok(SearchProviderType::DuckDuckGo)
50 } else if url_lower.contains("jina.ai") || url_lower.contains("s.jina.ai") {
51 Ok(SearchProviderType::Jina)
52 } else if url_lower.contains("api.tavily.com") || url_lower.contains("tavily.com") {
53 Ok(SearchProviderType::Tavily)
54 } else {
55 anyhow::bail!(
56 "Cannot auto-detect provider type from URL '{}'. \
57 Supported providers:\n\
58 - Brave: api.search.brave.com\n\
59 - Exa: api.exa.ai\n\
60 - Serper: google.serper.dev\n\
61 - SerpApi: serpapi.com\n\
62 - DuckDuckGo: api.duckduckgo.com\n\
63 - Jina: s.jina.ai\n\
64 - Tavily: api.tavily.com",
65 url
66 )
67 }
68 }
69
70 pub fn api_key_header(&self) -> &'static str {
72 match self {
73 SearchProviderType::Brave => "X-Subscription-Token",
74 SearchProviderType::Exa => "x-api-key",
75 SearchProviderType::Serper => "X-API-KEY",
76 SearchProviderType::SerpApi => "api_key",
77 SearchProviderType::DuckDuckGo => "", SearchProviderType::Jina => "Authorization",
79 SearchProviderType::Tavily => "Authorization",
80 }
81 }
82}
83
84#[allow(dead_code)]
85pub trait SearchProvider {
86 fn name(&self) -> &str;
87 fn search(
88 &self,
89 query: &str,
90 count: Option<usize>,
91 ) -> impl std::future::Future<Output = anyhow::Result<super::SearchResults>> + Send;
92}