1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8pub struct Config {
9 pub agents: AgentsConfig,
11 pub channels: ChannelsConfig,
13 pub providers: ProvidersConfig,
15 pub gateway: GatewayConfig,
17 pub tools: ToolsConfig,
19 #[serde(default)]
21 pub logging: LoggingConfig,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct LoggingConfig {
27 #[serde(default = "default_log_level")]
29 pub level: String,
30 #[serde(default = "default_log_format")]
32 pub format: String,
33 #[serde(default = "default_log_dir")]
35 pub dir: String,
36 #[serde(default)]
38 pub overrides: HashMap<String, String>,
39}
40
41fn default_log_level() -> String {
42 "info".to_string()
43}
44
45fn default_log_format() -> String {
46 "text".to_string()
47}
48
49fn default_log_dir() -> String {
50 "logs".to_string()
51}
52
53impl Default for LoggingConfig {
54 fn default() -> Self {
55 Self {
56 level: default_log_level(),
57 format: default_log_format(),
58 dir: default_log_dir(),
59 overrides: HashMap::new(),
60 }
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, Default)]
66pub struct AgentsConfig {
67 pub defaults: AgentDefaults,
69 #[serde(default)]
71 pub soul: AgentSoulConfig,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct AgentDefaults {
77 pub workspace: String,
79 #[serde(default)]
81 pub provider: Option<String>,
82 pub model: String,
84 pub max_tokens: u32,
86 pub temperature: f32,
88 pub max_tool_iterations: u32,
90 #[serde(default)]
92 pub reasoning_effort: Option<String>,
93}
94
95impl Default for AgentDefaults {
96 fn default() -> Self {
97 Self {
98 workspace: "~/.agent-diva/workspace".to_string(),
99 provider: Some("deepseek".to_string()),
100 model: "deepseek-chat".to_string(),
101 max_tokens: 8192,
102 temperature: 0.7,
103 max_tool_iterations: 20,
104 reasoning_effort: None,
105 }
106 }
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct AgentSoulConfig {
112 #[serde(default = "default_true")]
114 pub enabled: bool,
115 #[serde(default = "default_soul_max_chars")]
117 pub max_chars: usize,
118 #[serde(default = "default_true")]
120 pub notify_on_change: bool,
121 #[serde(default = "default_true")]
123 pub bootstrap_once: bool,
124 #[serde(default = "default_soul_window_secs")]
126 pub frequent_change_window_secs: u64,
127 #[serde(default = "default_soul_change_threshold")]
129 pub frequent_change_threshold: usize,
130 #[serde(default = "default_true")]
132 pub boundary_confirmation_hint: bool,
133}
134
135impl Default for AgentSoulConfig {
136 fn default() -> Self {
137 Self {
138 enabled: true,
139 max_chars: 4000,
140 notify_on_change: true,
141 bootstrap_once: true,
142 frequent_change_window_secs: 600,
143 frequent_change_threshold: 3,
144 boundary_confirmation_hint: true,
145 }
146 }
147}
148
149fn default_soul_max_chars() -> usize {
150 4000
151}
152
153fn default_soul_window_secs() -> u64 {
154 600
155}
156
157fn default_soul_change_threshold() -> usize {
158 3
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, Default)]
163pub struct ChannelsConfig {
164 #[serde(default)]
165 pub telegram: TelegramConfig,
166 #[serde(default)]
167 pub discord: DiscordConfig,
168 #[serde(default)]
169 pub whatsapp: WhatsAppConfig,
170 #[serde(default)]
171 pub feishu: FeishuConfig,
172 #[serde(default)]
173 pub dingtalk: DingTalkConfig,
174 #[serde(default)]
175 pub email: EmailConfig,
176 #[serde(default)]
177 pub slack: SlackConfig,
178 #[serde(default)]
179 pub qq: QQConfig,
180 #[serde(default)]
181 pub matrix: MatrixConfig,
182 #[serde(
183 default,
184 rename = "neuro-link",
185 alias = "neuro_link",
186 alias = "generic_pipe"
187 )]
188 pub neuro_link: NeuroLinkConfig,
189 #[serde(default)]
190 pub irc: IrcConfig,
191 #[serde(default)]
192 pub mattermost: MattermostConfig,
193 #[serde(default)]
194 pub nextcloud_talk: NextcloudTalkConfig,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize, Default)]
199pub struct TelegramConfig {
200 #[serde(default)]
201 pub enabled: bool,
202 #[serde(default)]
203 pub token: String,
204 #[serde(default)]
205 pub allow_from: Vec<String>,
206 #[serde(default)]
207 pub proxy: Option<String>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct DiscordConfig {
213 #[serde(default)]
214 pub enabled: bool,
215 #[serde(default)]
216 pub token: String,
217 #[serde(default)]
218 pub allow_from: Vec<String>,
219 #[serde(default = "default_discord_gateway")]
220 pub gateway_url: String,
221 #[serde(default = "default_discord_intents")]
222 pub intents: u64,
223}
224
225fn default_discord_gateway() -> String {
226 "wss://gateway.discord.gg/?v=10&encoding=json".to_string()
227}
228
229fn default_discord_intents() -> u64 {
230 37377 }
232
233impl Default for DiscordConfig {
234 fn default() -> Self {
235 Self {
236 enabled: false,
237 token: String::new(),
238 allow_from: Vec::new(),
239 gateway_url: default_discord_gateway(),
240 intents: default_discord_intents(),
241 }
242 }
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct WhatsAppConfig {
248 #[serde(default)]
249 pub enabled: bool,
250 #[serde(default = "default_whatsapp_bridge")]
251 pub bridge_url: String,
252 #[serde(default)]
253 pub allow_from: Vec<String>,
254}
255
256fn default_whatsapp_bridge() -> String {
257 "ws://localhost:3001".to_string()
258}
259
260impl Default for WhatsAppConfig {
261 fn default() -> Self {
262 Self {
263 enabled: false,
264 bridge_url: default_whatsapp_bridge(),
265 allow_from: Vec::new(),
266 }
267 }
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize, Default)]
272pub struct FeishuConfig {
273 #[serde(default)]
274 pub enabled: bool,
275 #[serde(default)]
276 pub app_id: String,
277 #[serde(default)]
278 pub app_secret: String,
279 #[serde(default)]
280 pub encrypt_key: String,
281 #[serde(default)]
282 pub verification_token: String,
283 #[serde(default)]
284 pub allow_from: Vec<String>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct DingTalkConfig {
290 #[serde(default)]
291 pub enabled: bool,
292 #[serde(default)]
293 pub client_id: String,
294 #[serde(default)]
295 pub client_secret: String,
296 #[serde(default)]
297 pub robot_code: String,
298 #[serde(default = "default_dingtalk_policy")]
299 pub dm_policy: String,
300 #[serde(default = "default_dingtalk_policy")]
301 pub group_policy: String,
302 #[serde(default)]
303 pub allow_from: Vec<String>,
304}
305
306fn default_dingtalk_policy() -> String {
307 "open".to_string()
308}
309
310impl Default for DingTalkConfig {
311 fn default() -> Self {
312 Self {
313 enabled: false,
314 client_id: String::new(),
315 client_secret: String::new(),
316 robot_code: String::new(),
317 dm_policy: default_dingtalk_policy(),
318 group_policy: default_dingtalk_policy(),
319 allow_from: Vec::new(),
320 }
321 }
322}
323
324#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct EmailConfig {
327 #[serde(default)]
328 pub enabled: bool,
329 #[serde(default)]
330 pub consent_granted: bool,
331 #[serde(default)]
333 pub imap_host: String,
334 #[serde(default = "default_imap_port")]
335 pub imap_port: u16,
336 #[serde(default)]
337 pub imap_username: String,
338 #[serde(default)]
339 pub imap_password: String,
340 #[serde(default = "default_imap_mailbox")]
341 pub imap_mailbox: String,
342 #[serde(default = "default_true")]
343 pub imap_use_ssl: bool,
344 #[serde(default)]
346 pub smtp_host: String,
347 #[serde(default = "default_smtp_port")]
348 pub smtp_port: u16,
349 #[serde(default)]
350 pub smtp_username: String,
351 #[serde(default)]
352 pub smtp_password: String,
353 #[serde(default = "default_true")]
354 pub smtp_use_tls: bool,
355 #[serde(default)]
356 pub smtp_use_ssl: bool,
357 #[serde(default)]
358 pub from_address: String,
359 #[serde(default = "default_true")]
361 pub auto_reply_enabled: bool,
362 #[serde(default = "default_poll_interval")]
363 pub poll_interval_seconds: u64,
364 #[serde(default = "default_true")]
365 pub mark_seen: bool,
366 #[serde(default = "default_max_body")]
367 pub max_body_chars: usize,
368 #[serde(default = "default_subject_prefix")]
369 pub subject_prefix: String,
370 #[serde(default)]
371 pub allow_from: Vec<String>,
372}
373
374fn default_imap_port() -> u16 {
375 993
376}
377fn default_imap_mailbox() -> String {
378 "INBOX".to_string()
379}
380fn default_smtp_port() -> u16 {
381 587
382}
383fn default_poll_interval() -> u64 {
384 30
385}
386fn default_max_body() -> usize {
387 12000
388}
389fn default_subject_prefix() -> String {
390 "Re: ".to_string()
391}
392fn default_true() -> bool {
393 true
394}
395
396impl Default for EmailConfig {
397 fn default() -> Self {
398 Self {
399 enabled: false,
400 consent_granted: false,
401 imap_host: String::new(),
402 imap_port: default_imap_port(),
403 imap_username: String::new(),
404 imap_password: String::new(),
405 imap_mailbox: default_imap_mailbox(),
406 imap_use_ssl: true,
407 smtp_host: String::new(),
408 smtp_port: default_smtp_port(),
409 smtp_username: String::new(),
410 smtp_password: String::new(),
411 smtp_use_tls: true,
412 smtp_use_ssl: false,
413 from_address: String::new(),
414 auto_reply_enabled: true,
415 poll_interval_seconds: default_poll_interval(),
416 mark_seen: true,
417 max_body_chars: default_max_body(),
418 subject_prefix: default_subject_prefix(),
419 allow_from: Vec::new(),
420 }
421 }
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
426pub struct SlackConfig {
427 #[serde(default)]
428 pub enabled: bool,
429 #[serde(default = "default_slack_mode")]
430 pub mode: String,
431 #[serde(default)]
432 pub webhook_path: String,
433 #[serde(default)]
434 pub bot_token: String,
435 #[serde(default)]
436 pub app_token: String,
437 #[serde(default = "default_true")]
438 pub user_token_read_only: bool,
439 #[serde(default = "default_slack_policy")]
440 pub group_policy: String,
441 #[serde(default)]
442 pub group_allow_from: Vec<String>,
443 #[serde(default)]
444 pub dm: SlackDMConfig,
445}
446
447fn default_slack_mode() -> String {
448 "socket".to_string()
449}
450fn default_slack_policy() -> String {
451 "mention".to_string()
452}
453
454impl Default for SlackConfig {
455 fn default() -> Self {
456 Self {
457 enabled: false,
458 mode: default_slack_mode(),
459 webhook_path: "/slack/events".to_string(),
460 bot_token: String::new(),
461 app_token: String::new(),
462 user_token_read_only: true,
463 group_policy: default_slack_policy(),
464 group_allow_from: Vec::new(),
465 dm: SlackDMConfig::default(),
466 }
467 }
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
472pub struct SlackDMConfig {
473 #[serde(default = "default_true")]
474 pub enabled: bool,
475 #[serde(default = "default_slack_dm_policy")]
476 pub policy: String,
477 #[serde(default)]
478 pub allow_from: Vec<String>,
479}
480
481fn default_slack_dm_policy() -> String {
482 "open".to_string()
483}
484
485impl Default for SlackDMConfig {
486 fn default() -> Self {
487 Self {
488 enabled: true,
489 policy: default_slack_dm_policy(),
490 allow_from: Vec::new(),
491 }
492 }
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize, Default)]
497pub struct QQConfig {
498 #[serde(default)]
499 pub enabled: bool,
500 #[serde(default)]
501 pub app_id: String,
502 #[serde(default)]
503 pub secret: String,
504 #[serde(default)]
505 pub allow_from: Vec<String>,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct NeuroLinkConfig {
511 #[serde(default)]
512 pub enabled: bool,
513 #[serde(default = "default_pipe_host")]
514 pub host: String,
515 #[serde(default = "default_pipe_port")]
516 pub port: u16,
517 #[serde(default)]
518 pub allow_from: Vec<String>,
519}
520
521fn default_pipe_host() -> String {
522 "0.0.0.0".to_string()
523}
524fn default_pipe_port() -> u16 {
525 9100
526}
527
528impl Default for NeuroLinkConfig {
529 fn default() -> Self {
530 Self {
531 enabled: false,
532 host: default_pipe_host(),
533 port: default_pipe_port(),
534 allow_from: Vec::new(),
535 }
536 }
537}
538
539#[derive(Debug, Clone, Serialize, Deserialize)]
541pub struct MatrixConfig {
542 #[serde(default)]
543 pub enabled: bool,
544 #[serde(default = "default_matrix_homeserver")]
545 pub homeserver: String,
546 #[serde(default)]
547 pub user_id: String,
548 #[serde(default)]
549 pub access_token: String,
550 #[serde(default)]
551 pub device_id: String,
552 #[serde(default = "default_true")]
553 pub e2ee_enabled: bool,
554 #[serde(default = "default_matrix_media_limit")]
555 pub max_media_bytes: usize,
556 #[serde(default)]
557 pub allow_from: Vec<String>,
558 #[serde(default)]
559 pub group_allow_from: Vec<String>,
560 #[serde(default = "default_matrix_sync_timeout")]
561 pub sync_timeout_ms: u64,
562 #[serde(default = "default_matrix_sync_stop_grace")]
563 pub sync_stop_grace_seconds: u64,
564}
565
566fn default_matrix_homeserver() -> String {
567 "https://matrix.org".to_string()
568}
569fn default_matrix_media_limit() -> usize {
570 20 * 1024 * 1024
571}
572fn default_matrix_sync_timeout() -> u64 {
573 30_000
574}
575fn default_matrix_sync_stop_grace() -> u64 {
576 8
577}
578
579impl Default for MatrixConfig {
580 fn default() -> Self {
581 Self {
582 enabled: false,
583 homeserver: default_matrix_homeserver(),
584 user_id: String::new(),
585 access_token: String::new(),
586 device_id: String::new(),
587 e2ee_enabled: true,
588 max_media_bytes: default_matrix_media_limit(),
589 allow_from: Vec::new(),
590 group_allow_from: Vec::new(),
591 sync_timeout_ms: default_matrix_sync_timeout(),
592 sync_stop_grace_seconds: default_matrix_sync_stop_grace(),
593 }
594 }
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct IrcConfig {
600 #[serde(default)]
601 pub enabled: bool,
602 #[serde(default)]
603 pub server: String,
604 #[serde(default = "default_irc_port")]
605 pub port: u16,
606 #[serde(default)]
607 pub nickname: String,
608 #[serde(default)]
609 pub username: String,
610 #[serde(default)]
611 pub channels: Vec<String>,
612 #[serde(default)]
613 pub server_password: Option<String>,
614 #[serde(default)]
615 pub nickserv_password: Option<String>,
616 #[serde(default)]
617 pub sasl_password: Option<String>,
618 #[serde(default = "default_true")]
619 pub use_tls: bool,
620 #[serde(default = "default_true")]
621 pub verify_tls: bool,
622 #[serde(default)]
623 pub allow_from: Vec<String>,
624}
625
626fn default_irc_port() -> u16 {
627 6697
628}
629
630impl Default for IrcConfig {
631 fn default() -> Self {
632 Self {
633 enabled: false,
634 server: String::new(),
635 port: default_irc_port(),
636 nickname: String::new(),
637 username: String::new(),
638 channels: Vec::new(),
639 server_password: None,
640 nickserv_password: None,
641 sasl_password: None,
642 use_tls: true,
643 verify_tls: true,
644 allow_from: Vec::new(),
645 }
646 }
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct MattermostConfig {
652 #[serde(default)]
653 pub enabled: bool,
654 #[serde(default)]
655 pub base_url: String,
656 #[serde(default)]
657 pub bot_token: String,
658 #[serde(default)]
659 pub channel_id: String,
660 #[serde(default = "default_true")]
661 pub thread_replies: bool,
662 #[serde(default)]
663 pub mention_only: bool,
664 #[serde(default = "default_mm_poll_interval")]
665 pub poll_interval_seconds: u64,
666 #[serde(default)]
667 pub allow_from: Vec<String>,
668}
669
670fn default_mm_poll_interval() -> u64 {
671 3
672}
673
674impl Default for MattermostConfig {
675 fn default() -> Self {
676 Self {
677 enabled: false,
678 base_url: String::new(),
679 bot_token: String::new(),
680 channel_id: String::new(),
681 thread_replies: true,
682 mention_only: false,
683 poll_interval_seconds: default_mm_poll_interval(),
684 allow_from: Vec::new(),
685 }
686 }
687}
688
689#[derive(Debug, Clone, Serialize, Deserialize)]
691pub struct NextcloudTalkConfig {
692 #[serde(default)]
693 pub enabled: bool,
694 #[serde(default)]
695 pub base_url: String,
696 #[serde(default)]
697 pub app_token: String,
698 #[serde(default)]
699 pub room_token: String,
700 #[serde(default = "default_nc_poll_interval")]
701 pub poll_interval_seconds: u64,
702 #[serde(default)]
703 pub allow_from: Vec<String>,
704}
705
706fn default_nc_poll_interval() -> u64 {
707 5
708}
709
710impl Default for NextcloudTalkConfig {
711 fn default() -> Self {
712 Self {
713 enabled: false,
714 base_url: String::new(),
715 app_token: String::new(),
716 room_token: String::new(),
717 poll_interval_seconds: default_nc_poll_interval(),
718 allow_from: Vec::new(),
719 }
720 }
721}
722
723#[derive(Debug, Clone, Serialize, Deserialize, Default)]
725pub struct ProvidersConfig {
726 #[serde(default)]
727 pub anthropic: ProviderConfig,
728 #[serde(default)]
729 pub openai: ProviderConfig,
730 #[serde(default)]
731 pub openrouter: ProviderConfig,
732 #[serde(default)]
733 pub deepseek: ProviderConfig,
734 #[serde(default)]
735 pub groq: ProviderConfig,
736 #[serde(default)]
737 pub zhipu: ProviderConfig,
738 #[serde(default)]
739 pub dashscope: ProviderConfig,
740 #[serde(default)]
741 pub vllm: ProviderConfig,
742 #[serde(default)]
743 pub gemini: ProviderConfig,
744 #[serde(default)]
745 pub moonshot: ProviderConfig,
746 #[serde(default)]
747 pub minimax: ProviderConfig,
748 #[serde(default)]
749 pub aihubmix: ProviderConfig,
750 #[serde(default)]
751 pub custom: ProviderConfig,
752 #[serde(default)]
753 pub custom_providers: HashMap<String, CustomProviderConfig>,
754}
755
756#[derive(Debug, Clone, Serialize, Deserialize, Default)]
758pub struct ProviderConfig {
759 #[serde(default)]
760 pub api_key: String,
761 #[serde(default)]
762 pub api_base: Option<String>,
763 #[serde(default)]
764 pub extra_headers: Option<HashMap<String, String>>,
765 #[serde(default)]
766 pub custom_models: Vec<String>,
767}
768
769#[derive(Debug, Clone, Serialize, Deserialize, Default)]
771pub struct CustomProviderConfig {
772 #[serde(default)]
773 pub display_name: String,
774 #[serde(default = "default_custom_provider_api_type")]
775 pub api_type: String,
776 #[serde(default)]
777 pub api_key: String,
778 #[serde(default)]
779 pub api_base: Option<String>,
780 #[serde(default)]
781 pub default_model: Option<String>,
782 #[serde(default)]
783 pub models: Vec<String>,
784 #[serde(default)]
785 pub extra_headers: Option<HashMap<String, String>>,
786}
787
788fn default_custom_provider_api_type() -> String {
789 "openai".to_string()
790}
791
792impl ProvidersConfig {
793 pub const BUILTIN_PROVIDER_IDS: [&'static str; 13] = [
794 "anthropic",
795 "openai",
796 "openrouter",
797 "deepseek",
798 "groq",
799 "zhipu",
800 "dashscope",
801 "vllm",
802 "gemini",
803 "moonshot",
804 "minimax",
805 "aihubmix",
806 "custom",
807 ];
808
809 pub fn builtin_provider_names() -> &'static [&'static str] {
810 &Self::BUILTIN_PROVIDER_IDS
811 }
812
813 pub fn get(&self, name: &str) -> Option<&ProviderConfig> {
814 match name {
815 "anthropic" => Some(&self.anthropic),
816 "openai" => Some(&self.openai),
817 "openrouter" => Some(&self.openrouter),
818 "deepseek" => Some(&self.deepseek),
819 "groq" => Some(&self.groq),
820 "zhipu" => Some(&self.zhipu),
821 "dashscope" => Some(&self.dashscope),
822 "vllm" => Some(&self.vllm),
823 "gemini" => Some(&self.gemini),
824 "moonshot" => Some(&self.moonshot),
825 "minimax" => Some(&self.minimax),
826 "aihubmix" => Some(&self.aihubmix),
827 "custom" => Some(&self.custom),
828 _ => None,
829 }
830 }
831
832 pub fn get_mut(&mut self, name: &str) -> Option<&mut ProviderConfig> {
833 match name {
834 "anthropic" => Some(&mut self.anthropic),
835 "openai" => Some(&mut self.openai),
836 "openrouter" => Some(&mut self.openrouter),
837 "deepseek" => Some(&mut self.deepseek),
838 "groq" => Some(&mut self.groq),
839 "zhipu" => Some(&mut self.zhipu),
840 "dashscope" => Some(&mut self.dashscope),
841 "vllm" => Some(&mut self.vllm),
842 "gemini" => Some(&mut self.gemini),
843 "moonshot" => Some(&mut self.moonshot),
844 "minimax" => Some(&mut self.minimax),
845 "aihubmix" => Some(&mut self.aihubmix),
846 "custom" => Some(&mut self.custom),
847 _ => None,
848 }
849 }
850
851 pub fn get_custom(&self, name: &str) -> Option<&CustomProviderConfig> {
852 self.custom_providers.get(name)
853 }
854
855 pub fn get_custom_mut(&mut self, name: &str) -> Option<&mut CustomProviderConfig> {
856 self.custom_providers.get_mut(name)
857 }
858
859 pub fn is_builtin_provider(name: &str) -> bool {
860 Self::BUILTIN_PROVIDER_IDS.contains(&name)
861 }
862}
863
864#[derive(Debug, Clone, Serialize, Deserialize)]
866pub struct GatewayConfig {
867 #[serde(default = "default_host")]
868 pub host: String,
869 #[serde(default = "default_port")]
870 pub port: u16,
871}
872
873fn default_host() -> String {
874 "0.0.0.0".to_string()
875}
876fn default_port() -> u16 {
877 18790
878}
879
880impl Default for GatewayConfig {
881 fn default() -> Self {
882 Self {
883 host: default_host(),
884 port: default_port(),
885 }
886 }
887}
888
889#[derive(Debug, Clone, Serialize, Deserialize, Default)]
891pub struct ToolsConfig {
892 #[serde(default)]
893 pub web: WebToolsConfig,
894 #[serde(default)]
895 pub exec: ExecToolConfig,
896 #[serde(default)]
897 pub restrict_to_workspace: bool,
898 #[serde(default, rename = "mcpServers", alias = "mcp_servers")]
899 pub mcp_servers: HashMap<String, MCPServerConfig>,
900 #[serde(default, rename = "mcpManager", alias = "mcp_manager")]
901 pub mcp_manager: MCPManagerConfig,
902}
903
904#[derive(Debug, Clone, Serialize, Deserialize, Default)]
905pub struct MCPManagerConfig {
906 #[serde(default)]
907 pub disabled_servers: Vec<String>,
908}
909
910impl ToolsConfig {
911 pub fn active_mcp_servers(&self) -> HashMap<String, MCPServerConfig> {
912 self.mcp_servers
913 .iter()
914 .filter(|(name, _)| !self.is_mcp_server_disabled(name))
915 .map(|(name, cfg)| (name.clone(), cfg.clone()))
916 .collect()
917 }
918
919 pub fn is_mcp_server_disabled(&self, name: &str) -> bool {
920 self.mcp_manager
921 .disabled_servers
922 .iter()
923 .any(|server| server == name)
924 }
925}
926
927#[derive(Debug, Clone, Serialize, Deserialize, Default)]
929pub struct MCPServerConfig {
930 #[serde(default)]
931 pub command: String,
932 #[serde(default)]
933 pub args: Vec<String>,
934 #[serde(default)]
935 pub env: HashMap<String, String>,
936 #[serde(default)]
937 pub url: String,
938 #[serde(default = "default_tool_timeout")]
940 pub tool_timeout: u64,
941}
942
943fn default_tool_timeout() -> u64 {
944 30
945}
946
947#[derive(Debug, Clone, Serialize, Deserialize, Default)]
949pub struct WebToolsConfig {
950 #[serde(default)]
951 pub search: WebSearchConfig,
952 #[serde(default)]
953 pub fetch: WebFetchConfig,
954}
955
956#[derive(Debug, Clone, Serialize, Deserialize)]
957pub struct WebFetchConfig {
958 #[serde(default = "default_enabled")]
959 pub enabled: bool,
960}
961
962#[derive(Debug, Clone, Serialize, Deserialize)]
964pub struct WebSearchConfig {
965 #[serde(default = "default_search_provider")]
966 pub provider: String,
967 #[serde(default = "default_enabled")]
968 pub enabled: bool,
969 #[serde(default)]
970 pub api_key: String,
971 #[serde(default = "default_max_results")]
972 pub max_results: u32,
973}
974
975fn default_search_provider() -> String {
976 "bocha".to_string()
977}
978
979fn default_enabled() -> bool {
980 true
981}
982
983fn default_max_results() -> u32 {
984 5
985}
986
987impl Default for WebSearchConfig {
988 fn default() -> Self {
989 Self {
990 provider: default_search_provider(),
991 enabled: default_enabled(),
992 api_key: String::new(),
993 max_results: default_max_results(),
994 }
995 }
996}
997
998impl Default for WebFetchConfig {
999 fn default() -> Self {
1000 Self {
1001 enabled: default_enabled(),
1002 }
1003 }
1004}
1005
1006#[derive(Debug, Clone, Serialize, Deserialize)]
1008pub struct ExecToolConfig {
1009 #[serde(default = "default_timeout")]
1010 pub timeout: u64,
1011}
1012
1013fn default_timeout() -> u64 {
1014 60
1015}
1016
1017impl Default for ExecToolConfig {
1018 fn default() -> Self {
1019 Self {
1020 timeout: default_timeout(),
1021 }
1022 }
1023}