Skip to main content

zai_rs/tool/web_search/
request.rs

1use serde::{Deserialize, Serialize};
2use validator::Validate;
3
4/// Web search engine options supported by the API
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6#[serde(rename_all = "snake_case")]
7pub enum SearchEngine {
8    /// Zhipu basic search engine
9    SearchStd,
10    /// Zhipu advanced search engine
11    SearchPro,
12    /// Sougou search engine
13    SearchProSogou,
14    /// Quark search engine
15    SearchProQuark,
16}
17
18/// Search result recency filter options
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
20#[serde(rename_all = "snake_case")]
21pub enum SearchRecencyFilter {
22    /// Search within one day
23    OneDay,
24    /// Search within one week
25    OneWeek,
26    /// Search within one month
27    OneMonth,
28    /// Search within one year
29    OneYear,
30    /// No time limit (default)
31    NoLimit,
32}
33
34/// Content size options for search results
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
36#[serde(rename_all = "snake_case")]
37pub enum ContentSize {
38    /// Medium content size with summary information for basic reasoning needs
39    Medium,
40    /// High content size with maximized context and detailed information
41    High,
42}
43
44/// Search intent result
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct SearchIntent {
47    /// The search query
48    pub query: String,
49    /// The detected intent type
50    pub intent: String,
51    /// Extracted keywords
52    pub keywords: String,
53}
54
55/// Individual search result item
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct SearchResult {
58    /// Title of the search result
59    pub title: String,
60    /// Content summary
61    pub content: String,
62    /// URL link to the result
63    pub link: String,
64    /// Website/media name
65    pub media: String,
66    /// Website icon URL
67    pub icon: String,
68    /// Reference index number
69    pub refer: String,
70    /// Publication date
71    pub publish_date: String,
72}
73
74/// Web search request body
75#[derive(Debug, Clone, Serialize, Validate)]
76pub struct WebSearchBody {
77    /// Search query content (max 70 characters)
78    #[validate(length(max = 70, message = "search_query cannot exceed 70 characters"))]
79    pub search_query: String,
80
81    /// Search engine to use
82    pub search_engine: SearchEngine,
83
84    /// Whether to perform search intent recognition
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub search_intent: Option<bool>,
87
88    /// Number of results to return (1-50)
89    #[validate(range(min = 1, max = 50, message = "count must be between 1 and 50"))]
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub count: Option<i32>,
92
93    /// Domain filter for search results (whitelist)
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub search_domain_filter: Option<String>,
96
97    /// Time range filter for search results
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub search_recency_filter: Option<SearchRecencyFilter>,
100
101    /// Content size control
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub content_size: Option<ContentSize>,
104
105    /// Unique request identifier
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub request_id: Option<String>,
108
109    /// End user unique ID (6-128 characters)
110    #[validate(length(
111        min = 6,
112        max = 128,
113        message = "user_id must be between 6 and 128 characters"
114    ))]
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub user_id: Option<String>,
117}
118
119impl WebSearchBody {
120    /// Create a new web search request body with required parameters
121    pub fn new(search_query: String, search_engine: SearchEngine) -> Self {
122        Self {
123            search_query,
124            search_engine,
125            search_intent: None,
126            count: None,
127            search_domain_filter: None,
128            search_recency_filter: None,
129            content_size: None,
130            request_id: None,
131            user_id: None,
132        }
133    }
134
135    /// Enable search intent recognition
136    pub fn with_search_intent(mut self, enabled: bool) -> Self {
137        self.search_intent = Some(enabled);
138        self
139    }
140
141    /// Set the number of results to return
142    pub fn with_count(mut self, count: i32) -> Self {
143        self.count = Some(count);
144        self
145    }
146
147    /// Set domain filter for search results
148    pub fn with_domain_filter(mut self, domain: String) -> Self {
149        self.search_domain_filter = Some(domain);
150        self
151    }
152
153    /// Set time range filter for search results
154    pub fn with_recency_filter(mut self, filter: SearchRecencyFilter) -> Self {
155        self.search_recency_filter = Some(filter);
156        self
157    }
158
159    /// Set content size preference
160    pub fn with_content_size(mut self, size: ContentSize) -> Self {
161        self.content_size = Some(size);
162        self
163    }
164
165    /// Set custom request ID
166    pub fn with_request_id(mut self, request_id: String) -> Self {
167        self.request_id = Some(request_id);
168        self
169    }
170
171    /// Set user ID
172    pub fn with_user_id(mut self, user_id: String) -> Self {
173        self.user_id = Some(user_id);
174        self
175    }
176
177    /// Validate the request body constraints
178    pub fn validate_constraints(&self) -> crate::ZaiResult<()> {
179        self.validate()
180            .map_err(|e| crate::client::error::ZaiError::ApiError {
181                code: 1200,
182                message: format!("Validation error: {}", e),
183            })?;
184
185        // Additional validation for count based on search engine
186        if let Some(count) = self.count
187            && matches!(self.search_engine, SearchEngine::SearchProSogou)
188        {
189            match count {
190                10 | 20 | 30 | 40 | 50 => {},
191                _ => {
192                    return Err(crate::client::error::ZaiError::ApiError {
193                        code: 1200,
194                        message: "search_pro_sogou only supports count values: 10, 20, 30, 40, 50"
195                            .to_string(),
196                    });
197                },
198            }
199        }
200
201        Ok(())
202    }
203}