1use derive_builder::Builder;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6use crate::types::mcp::McpServerConfig;
7use crate::types::permission::PermissionMode;
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
11#[serde(rename_all = "snake_case")]
12pub enum AuthType {
13 #[default]
14 Openai,
15 QwenOauth,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(tag = "type", rename_all = "snake_case")]
21pub enum SystemPromptConfig {
22 Custom(String),
24 Preset {
26 preset: String,
27 append: Option<String>,
28 },
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
33#[builder(default)]
34pub struct TimeoutConfig {
35 #[builder(default = "60000")]
37 pub can_use_tool: u64,
38
39 #[builder(default = "60000")]
41 pub mcp_request: u64,
42
43 #[builder(default = "60000")]
45 pub control_request: u64,
46
47 #[builder(default = "15000")]
49 pub stream_close: u64,
50}
51
52impl Default for TimeoutConfig {
53 fn default() -> Self {
54 TimeoutConfig {
55 can_use_tool: 60000,
56 mcp_request: 60000,
57 control_request: 60000,
58 stream_close: 15000,
59 }
60 }
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
65#[builder(default)]
66#[derive(Default)]
67pub struct SubagentConfig {
68 #[builder(default)]
69 pub name: String,
70 #[builder(default)]
71 pub description: String,
72 #[builder(default)]
73 pub tools: Option<Vec<String>>,
74}
75
76#[derive(Debug, Clone, Builder, Serialize, Deserialize)]
78#[builder(default)]
79pub struct QueryOptions {
80 #[builder(default, setter(into))]
82 pub cwd: Option<PathBuf>,
83
84 #[builder(default, setter(into))]
86 pub model: Option<String>,
87
88 #[builder(default, setter(into))]
90 pub path_to_qwen_executable: Option<String>,
91
92 #[builder(default)]
94 pub permission_mode: PermissionMode,
95
96 #[builder(default)]
98 pub env: Option<HashMap<String, String>>,
99
100 #[builder(default)]
102 pub system_prompt: Option<SystemPromptConfig>,
103
104 #[builder(default)]
106 pub mcp_servers: Option<HashMap<String, McpServerConfig>>,
107
108 #[builder(default)]
110 pub debug: bool,
111
112 #[builder(default = "-1")]
114 pub max_session_turns: i32,
115
116 #[builder(default)]
118 pub core_tools: Option<Vec<String>>,
119
120 #[builder(default)]
122 pub exclude_tools: Option<Vec<String>>,
123
124 #[builder(default)]
126 pub allowed_tools: Option<Vec<String>>,
127
128 #[builder(default)]
130 pub auth_type: AuthType,
131
132 #[builder(default)]
134 pub agents: Option<Vec<SubagentConfig>>,
135
136 #[builder(default)]
138 pub include_partial_messages: bool,
139
140 #[builder(default, setter(into))]
142 pub resume: Option<String>,
143
144 #[builder(default, setter(into))]
146 pub session_id: Option<String>,
147
148 #[builder(default)]
150 pub timeouts: Option<TimeoutConfig>,
151}
152
153impl Default for QueryOptions {
154 fn default() -> Self {
155 QueryOptions {
156 cwd: None,
157 model: None,
158 path_to_qwen_executable: None,
159 permission_mode: PermissionMode::default(),
160 env: None,
161 system_prompt: None,
162 mcp_servers: None,
163 debug: false,
164 max_session_turns: -1,
165 core_tools: None,
166 exclude_tools: None,
167 allowed_tools: None,
168 auth_type: AuthType::default(),
169 agents: None,
170 include_partial_messages: false,
171 resume: None,
172 session_id: None,
173 timeouts: None,
174 }
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_auth_type_default() {
184 let auth = AuthType::default();
185 assert_eq!(auth, AuthType::Openai);
186 }
187
188 #[test]
189 fn test_auth_type_qwen_oauth() {
190 let auth = AuthType::QwenOauth;
191 assert_eq!(auth, AuthType::QwenOauth);
192 }
193
194 #[test]
195 fn test_auth_type_serialization() {
196 let auth = AuthType::Openai;
197 let serialized = serde_json::to_string(&auth).unwrap();
198 assert_eq!(serialized, "\"openai\"");
199
200 let deserialized: AuthType = serde_json::from_str(&serialized).unwrap();
201 assert_eq!(deserialized, AuthType::Openai);
202 }
203
204 #[test]
205 fn test_system_prompt_config_custom() {
206 let config = SystemPromptConfig::Custom("Custom prompt".to_string());
207
208 match &config {
209 SystemPromptConfig::Custom(prompt) => {
210 assert_eq!(prompt, "Custom prompt");
211 }
212 _ => panic!("Expected Custom variant"),
213 }
214 }
215
216 #[test]
217 fn test_system_prompt_config_preset_without_append() {
218 let config = SystemPromptConfig::Preset {
219 preset: "qwen_code".to_string(),
220 append: None,
221 };
222
223 match &config {
224 SystemPromptConfig::Preset { preset, append } => {
225 assert_eq!(preset, "qwen_code");
226 assert!(append.is_none());
227 }
228 _ => panic!("Expected Preset variant"),
229 }
230 }
231
232 #[test]
233 fn test_system_prompt_config_preset_with_append() {
234 let config = SystemPromptConfig::Preset {
235 preset: "qwen_code".to_string(),
236 append: Some("Additional instructions".to_string()),
237 };
238
239 match &config {
240 SystemPromptConfig::Preset { preset, append } => {
241 assert_eq!(preset, "qwen_code");
242 assert_eq!(append, &Some("Additional instructions".to_string()));
243 }
244 _ => panic!("Expected Preset variant"),
245 }
246 }
247
248 #[test]
249 fn test_timeout_config_default() {
250 let config = TimeoutConfig::default();
251
252 assert_eq!(config.can_use_tool, 60000);
253 assert_eq!(config.mcp_request, 60000);
254 assert_eq!(config.control_request, 60000);
255 assert_eq!(config.stream_close, 15000);
256 }
257
258 #[test]
259 fn test_timeout_config_builder() {
260 let config = TimeoutConfigBuilder::default()
261 .can_use_tool(30000)
262 .mcp_request(120000)
263 .build()
264 .unwrap();
265
266 assert_eq!(config.can_use_tool, 30000);
267 assert_eq!(config.mcp_request, 120000);
268 assert_eq!(config.control_request, 60000); assert_eq!(config.stream_close, 15000); }
271
272 #[test]
273 fn test_subagent_config_default() {
274 let config = SubagentConfig::default();
275
276 assert_eq!(config.name, "");
277 assert_eq!(config.description, "");
278 assert!(config.tools.is_none());
279 }
280
281 #[test]
282 fn test_subagent_config_builder() {
283 let config = SubagentConfigBuilder::default()
284 .name("test-agent".to_string())
285 .description("Test agent".to_string())
286 .tools(Some(vec!["tool1".to_string(), "tool2".to_string()]))
287 .build()
288 .unwrap();
289
290 assert_eq!(config.name, "test-agent");
291 assert_eq!(config.description, "Test agent");
292 assert_eq!(
293 config.tools,
294 Some(vec!["tool1".to_string(), "tool2".to_string()])
295 );
296 }
297
298 #[test]
299 fn test_query_options_default() {
300 let options = QueryOptions::default();
301
302 assert!(options.cwd.is_none());
303 assert!(options.model.is_none());
304 assert!(options.path_to_qwen_executable.is_none());
305 assert_eq!(options.permission_mode, PermissionMode::default());
306 assert!(options.env.is_none());
307 assert!(options.system_prompt.is_none());
308 assert!(options.mcp_servers.is_none());
309 assert!(!options.debug);
310 assert_eq!(options.max_session_turns, -1);
311 assert!(options.core_tools.is_none());
312 assert!(options.exclude_tools.is_none());
313 assert!(options.allowed_tools.is_none());
314 assert_eq!(options.auth_type, AuthType::default());
315 assert!(options.agents.is_none());
316 assert!(!options.include_partial_messages);
317 assert!(options.resume.is_none());
318 assert!(options.session_id.is_none());
319 assert!(options.timeouts.is_none());
320 }
321
322 #[test]
323 fn test_query_options_builder() {
324 let options = QueryOptionsBuilder::default()
325 .model("qwen-max".to_string())
326 .debug(true)
327 .max_session_turns(10)
328 .include_partial_messages(true)
329 .build()
330 .unwrap();
331
332 assert_eq!(options.model, Some("qwen-max".to_string()));
333 assert!(options.debug);
334 assert_eq!(options.max_session_turns, 10);
335 assert!(options.include_partial_messages);
336 assert!(options.cwd.is_none());
338 assert_eq!(options.permission_mode, PermissionMode::default());
339 }
340
341 #[test]
342 fn test_query_options_with_cwd_pathbuf() {
343 let path = PathBuf::from("/tmp/test");
344 let options = QueryOptionsBuilder::default()
345 .cwd(path.clone())
346 .build()
347 .unwrap();
348
349 assert_eq!(options.cwd, Some(path));
350 }
351
352 #[test]
353 fn test_query_options_serialization() {
354 let options = QueryOptions {
355 model: Some("qwen-plus".to_string()),
356 debug: true,
357 max_session_turns: 5,
358 ..Default::default()
359 };
360
361 let serialized = serde_json::to_string(&options).unwrap();
362 assert!(serialized.contains("\"model\":\"qwen-plus\""));
363 assert!(serialized.contains("\"debug\":true"));
364 assert!(serialized.contains("\"max_session_turns\":5"));
365 }
366
367 #[test]
368 fn test_timeout_config_all_custom() {
369 let config = TimeoutConfigBuilder::default()
370 .can_use_tool(10000)
371 .mcp_request(20000)
372 .control_request(30000)
373 .stream_close(5000)
374 .build()
375 .unwrap();
376
377 assert_eq!(config.can_use_tool, 10000);
378 assert_eq!(config.mcp_request, 20000);
379 assert_eq!(config.control_request, 30000);
380 assert_eq!(config.stream_close, 5000);
381 }
382}