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 #[serde(default)]
225 pub guild_id: Option<String>,
226 #[serde(default)]
228 pub mention_only: bool,
229 #[serde(default)]
231 pub listen_to_bots: bool,
232 #[serde(default)]
234 pub group_reply_allowed_sender_ids: Vec<String>,
235}
236
237fn default_discord_gateway() -> String {
238 "wss://gateway.discord.gg/?v=10&encoding=json".to_string()
239}
240
241fn default_discord_intents() -> u64 {
242 37377 }
244
245impl Default for DiscordConfig {
246 fn default() -> Self {
247 Self {
248 enabled: false,
249 token: String::new(),
250 allow_from: Vec::new(),
251 gateway_url: default_discord_gateway(),
252 intents: default_discord_intents(),
253 guild_id: None,
254 mention_only: false,
255 listen_to_bots: false,
256 group_reply_allowed_sender_ids: Vec::new(),
257 }
258 }
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct WhatsAppConfig {
264 #[serde(default)]
265 pub enabled: bool,
266 #[serde(default = "default_whatsapp_bridge")]
267 pub bridge_url: String,
268 #[serde(default)]
269 pub allow_from: Vec<String>,
270}
271
272fn default_whatsapp_bridge() -> String {
273 "ws://localhost:3001".to_string()
274}
275
276impl Default for WhatsAppConfig {
277 fn default() -> Self {
278 Self {
279 enabled: false,
280 bridge_url: default_whatsapp_bridge(),
281 allow_from: Vec::new(),
282 }
283 }
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize, Default)]
288pub struct FeishuConfig {
289 #[serde(default)]
290 pub enabled: bool,
291 #[serde(default)]
292 pub app_id: String,
293 #[serde(default)]
294 pub app_secret: String,
295 #[serde(default)]
296 pub encrypt_key: String,
297 #[serde(default)]
298 pub verification_token: String,
299 #[serde(default)]
300 pub allow_from: Vec<String>,
301 #[serde(default)]
303 pub port: Option<u16>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct DingTalkConfig {
309 #[serde(default)]
310 pub enabled: bool,
311 #[serde(default)]
312 pub client_id: String,
313 #[serde(default)]
314 pub client_secret: String,
315 #[serde(default)]
316 pub robot_code: String,
317 #[serde(default = "default_dingtalk_policy")]
318 pub dm_policy: String,
319 #[serde(default = "default_dingtalk_policy")]
320 pub group_policy: String,
321 #[serde(default)]
322 pub allow_from: Vec<String>,
323}
324
325fn default_dingtalk_policy() -> String {
326 "open".to_string()
327}
328
329impl Default for DingTalkConfig {
330 fn default() -> Self {
331 Self {
332 enabled: false,
333 client_id: String::new(),
334 client_secret: String::new(),
335 robot_code: String::new(),
336 dm_policy: default_dingtalk_policy(),
337 group_policy: default_dingtalk_policy(),
338 allow_from: Vec::new(),
339 }
340 }
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct EmailConfig {
346 #[serde(default)]
347 pub enabled: bool,
348 #[serde(default)]
349 pub consent_granted: bool,
350 #[serde(default)]
352 pub imap_host: String,
353 #[serde(default = "default_imap_port")]
354 pub imap_port: u16,
355 #[serde(default)]
356 pub imap_username: String,
357 #[serde(default)]
358 pub imap_password: String,
359 #[serde(default = "default_imap_mailbox")]
360 pub imap_mailbox: String,
361 #[serde(default = "default_true")]
362 pub imap_use_ssl: bool,
363 #[serde(default)]
365 pub smtp_host: String,
366 #[serde(default = "default_smtp_port")]
367 pub smtp_port: u16,
368 #[serde(default)]
369 pub smtp_username: String,
370 #[serde(default)]
371 pub smtp_password: String,
372 #[serde(default = "default_true")]
373 pub smtp_use_tls: bool,
374 #[serde(default)]
375 pub smtp_use_ssl: bool,
376 #[serde(default)]
377 pub from_address: String,
378 #[serde(default = "default_true")]
380 pub auto_reply_enabled: bool,
381 #[serde(default = "default_poll_interval")]
382 pub poll_interval_seconds: u64,
383 #[serde(default = "default_true")]
384 pub mark_seen: bool,
385 #[serde(default = "default_max_body")]
386 pub max_body_chars: usize,
387 #[serde(default = "default_subject_prefix")]
388 pub subject_prefix: String,
389 #[serde(default)]
390 pub allow_from: Vec<String>,
391}
392
393fn default_imap_port() -> u16 {
394 993
395}
396fn default_imap_mailbox() -> String {
397 "INBOX".to_string()
398}
399fn default_smtp_port() -> u16 {
400 587
401}
402fn default_poll_interval() -> u64 {
403 30
404}
405fn default_max_body() -> usize {
406 12000
407}
408fn default_subject_prefix() -> String {
409 "Re: ".to_string()
410}
411fn default_true() -> bool {
412 true
413}
414
415impl Default for EmailConfig {
416 fn default() -> Self {
417 Self {
418 enabled: false,
419 consent_granted: false,
420 imap_host: String::new(),
421 imap_port: default_imap_port(),
422 imap_username: String::new(),
423 imap_password: String::new(),
424 imap_mailbox: default_imap_mailbox(),
425 imap_use_ssl: true,
426 smtp_host: String::new(),
427 smtp_port: default_smtp_port(),
428 smtp_username: String::new(),
429 smtp_password: String::new(),
430 smtp_use_tls: true,
431 smtp_use_ssl: false,
432 from_address: String::new(),
433 auto_reply_enabled: true,
434 poll_interval_seconds: default_poll_interval(),
435 mark_seen: true,
436 max_body_chars: default_max_body(),
437 subject_prefix: default_subject_prefix(),
438 allow_from: Vec::new(),
439 }
440 }
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct SlackConfig {
446 #[serde(default)]
447 pub enabled: bool,
448 #[serde(default = "default_slack_mode")]
449 pub mode: String,
450 #[serde(default)]
451 pub webhook_path: String,
452 #[serde(default)]
453 pub bot_token: String,
454 #[serde(default)]
455 pub app_token: String,
456 #[serde(default = "default_true")]
457 pub user_token_read_only: bool,
458 #[serde(default = "default_slack_policy")]
459 pub group_policy: String,
460 #[serde(default)]
461 pub group_allow_from: Vec<String>,
462 #[serde(default)]
463 pub dm: SlackDMConfig,
464}
465
466fn default_slack_mode() -> String {
467 "socket".to_string()
468}
469fn default_slack_policy() -> String {
470 "mention".to_string()
471}
472
473impl Default for SlackConfig {
474 fn default() -> Self {
475 Self {
476 enabled: false,
477 mode: default_slack_mode(),
478 webhook_path: "/slack/events".to_string(),
479 bot_token: String::new(),
480 app_token: String::new(),
481 user_token_read_only: true,
482 group_policy: default_slack_policy(),
483 group_allow_from: Vec::new(),
484 dm: SlackDMConfig::default(),
485 }
486 }
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct SlackDMConfig {
492 #[serde(default = "default_true")]
493 pub enabled: bool,
494 #[serde(default = "default_slack_dm_policy")]
495 pub policy: String,
496 #[serde(default)]
497 pub allow_from: Vec<String>,
498}
499
500fn default_slack_dm_policy() -> String {
501 "open".to_string()
502}
503
504impl Default for SlackDMConfig {
505 fn default() -> Self {
506 Self {
507 enabled: true,
508 policy: default_slack_dm_policy(),
509 allow_from: Vec::new(),
510 }
511 }
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize, Default)]
516pub struct QQConfig {
517 #[serde(default)]
518 pub enabled: bool,
519 #[serde(default)]
520 pub app_id: String,
521 #[serde(default)]
522 pub secret: String,
523 #[serde(default)]
524 pub allow_from: Vec<String>,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct NeuroLinkConfig {
530 #[serde(default)]
531 pub enabled: bool,
532 #[serde(default = "default_pipe_host")]
533 pub host: String,
534 #[serde(default = "default_pipe_port")]
535 pub port: u16,
536 #[serde(default)]
537 pub allow_from: Vec<String>,
538}
539
540fn default_pipe_host() -> String {
541 "0.0.0.0".to_string()
542}
543fn default_pipe_port() -> u16 {
544 9100
545}
546
547impl Default for NeuroLinkConfig {
548 fn default() -> Self {
549 Self {
550 enabled: false,
551 host: default_pipe_host(),
552 port: default_pipe_port(),
553 allow_from: Vec::new(),
554 }
555 }
556}
557
558#[derive(Debug, Clone, Serialize, Deserialize)]
560pub struct MatrixConfig {
561 #[serde(default)]
562 pub enabled: bool,
563 #[serde(default = "default_matrix_homeserver")]
564 pub homeserver: String,
565 #[serde(default)]
566 pub user_id: String,
567 #[serde(default)]
568 pub access_token: String,
569 #[serde(default)]
570 pub device_id: String,
571 #[serde(default = "default_true")]
572 pub e2ee_enabled: bool,
573 #[serde(default = "default_matrix_media_limit")]
574 pub max_media_bytes: usize,
575 #[serde(default)]
576 pub allow_from: Vec<String>,
577 #[serde(default)]
578 pub group_allow_from: Vec<String>,
579 #[serde(default = "default_matrix_sync_timeout")]
580 pub sync_timeout_ms: u64,
581 #[serde(default = "default_matrix_sync_stop_grace")]
582 pub sync_stop_grace_seconds: u64,
583}
584
585fn default_matrix_homeserver() -> String {
586 "https://matrix.org".to_string()
587}
588fn default_matrix_media_limit() -> usize {
589 20 * 1024 * 1024
590}
591fn default_matrix_sync_timeout() -> u64 {
592 30_000
593}
594fn default_matrix_sync_stop_grace() -> u64 {
595 8
596}
597
598impl Default for MatrixConfig {
599 fn default() -> Self {
600 Self {
601 enabled: false,
602 homeserver: default_matrix_homeserver(),
603 user_id: String::new(),
604 access_token: String::new(),
605 device_id: String::new(),
606 e2ee_enabled: true,
607 max_media_bytes: default_matrix_media_limit(),
608 allow_from: Vec::new(),
609 group_allow_from: Vec::new(),
610 sync_timeout_ms: default_matrix_sync_timeout(),
611 sync_stop_grace_seconds: default_matrix_sync_stop_grace(),
612 }
613 }
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
618pub struct IrcConfig {
619 #[serde(default)]
620 pub enabled: bool,
621 #[serde(default)]
622 pub server: String,
623 #[serde(default = "default_irc_port")]
624 pub port: u16,
625 #[serde(default)]
626 pub nickname: String,
627 #[serde(default)]
628 pub username: String,
629 #[serde(default)]
630 pub channels: Vec<String>,
631 #[serde(default)]
632 pub server_password: Option<String>,
633 #[serde(default)]
634 pub nickserv_password: Option<String>,
635 #[serde(default)]
636 pub sasl_password: Option<String>,
637 #[serde(default = "default_true")]
638 pub use_tls: bool,
639 #[serde(default = "default_true")]
640 pub verify_tls: bool,
641 #[serde(default)]
642 pub allow_from: Vec<String>,
643}
644
645fn default_irc_port() -> u16 {
646 6697
647}
648
649impl Default for IrcConfig {
650 fn default() -> Self {
651 Self {
652 enabled: false,
653 server: String::new(),
654 port: default_irc_port(),
655 nickname: String::new(),
656 username: String::new(),
657 channels: Vec::new(),
658 server_password: None,
659 nickserv_password: None,
660 sasl_password: None,
661 use_tls: true,
662 verify_tls: true,
663 allow_from: Vec::new(),
664 }
665 }
666}
667
668#[derive(Debug, Clone, Serialize, Deserialize)]
670pub struct MattermostConfig {
671 #[serde(default)]
672 pub enabled: bool,
673 #[serde(default)]
674 pub base_url: String,
675 #[serde(default)]
676 pub bot_token: String,
677 #[serde(default)]
678 pub channel_id: String,
679 #[serde(default = "default_true")]
680 pub thread_replies: bool,
681 #[serde(default)]
682 pub mention_only: bool,
683 #[serde(default = "default_mm_poll_interval")]
684 pub poll_interval_seconds: u64,
685 #[serde(default)]
686 pub allow_from: Vec<String>,
687}
688
689fn default_mm_poll_interval() -> u64 {
690 3
691}
692
693impl Default for MattermostConfig {
694 fn default() -> Self {
695 Self {
696 enabled: false,
697 base_url: String::new(),
698 bot_token: String::new(),
699 channel_id: String::new(),
700 thread_replies: true,
701 mention_only: false,
702 poll_interval_seconds: default_mm_poll_interval(),
703 allow_from: Vec::new(),
704 }
705 }
706}
707
708#[derive(Debug, Clone, Serialize, Deserialize)]
710pub struct NextcloudTalkConfig {
711 #[serde(default)]
712 pub enabled: bool,
713 #[serde(default)]
714 pub base_url: String,
715 #[serde(default)]
716 pub app_token: String,
717 #[serde(default)]
718 pub room_token: String,
719 #[serde(default = "default_nc_poll_interval")]
720 pub poll_interval_seconds: u64,
721 #[serde(default)]
722 pub allow_from: Vec<String>,
723}
724
725fn default_nc_poll_interval() -> u64 {
726 5
727}
728
729impl Default for NextcloudTalkConfig {
730 fn default() -> Self {
731 Self {
732 enabled: false,
733 base_url: String::new(),
734 app_token: String::new(),
735 room_token: String::new(),
736 poll_interval_seconds: default_nc_poll_interval(),
737 allow_from: Vec::new(),
738 }
739 }
740}
741
742#[derive(Debug, Clone, Serialize, Deserialize, Default)]
744pub struct ProvidersConfig {
745 #[serde(default)]
746 pub anthropic: ProviderConfig,
747 #[serde(default)]
748 pub openai: ProviderConfig,
749 #[serde(default)]
750 pub openrouter: ProviderConfig,
751 #[serde(default)]
752 pub deepseek: ProviderConfig,
753 #[serde(default)]
754 pub groq: ProviderConfig,
755 #[serde(default)]
756 pub zhipu: ProviderConfig,
757 #[serde(default)]
758 pub dashscope: ProviderConfig,
759 #[serde(default)]
760 pub vllm: ProviderConfig,
761 #[serde(default)]
762 pub gemini: ProviderConfig,
763 #[serde(default)]
764 pub moonshot: ProviderConfig,
765 #[serde(default)]
766 pub minimax: ProviderConfig,
767 #[serde(default)]
768 pub aihubmix: ProviderConfig,
769 #[serde(default)]
770 pub custom: ProviderConfig,
771 #[serde(default)]
772 pub custom_providers: HashMap<String, CustomProviderConfig>,
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize, Default)]
777pub struct ProviderConfig {
778 #[serde(default)]
779 pub api_key: String,
780 #[serde(default)]
781 pub api_base: Option<String>,
782 #[serde(default)]
783 pub extra_headers: Option<HashMap<String, String>>,
784 #[serde(default)]
785 pub custom_models: Vec<String>,
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize, Default)]
790pub struct CustomProviderConfig {
791 #[serde(default)]
792 pub display_name: String,
793 #[serde(default = "default_custom_provider_api_type")]
794 pub api_type: String,
795 #[serde(default)]
796 pub api_key: String,
797 #[serde(default)]
798 pub api_base: Option<String>,
799 #[serde(default)]
800 pub default_model: Option<String>,
801 #[serde(default)]
802 pub models: Vec<String>,
803 #[serde(default)]
804 pub extra_headers: Option<HashMap<String, String>>,
805}
806
807fn default_custom_provider_api_type() -> String {
808 "openai".to_string()
809}
810
811impl ProvidersConfig {
812 pub const BUILTIN_PROVIDER_IDS: [&'static str; 13] = [
813 "anthropic",
814 "openai",
815 "openrouter",
816 "deepseek",
817 "groq",
818 "zhipu",
819 "dashscope",
820 "vllm",
821 "gemini",
822 "moonshot",
823 "minimax",
824 "aihubmix",
825 "custom",
826 ];
827
828 pub fn builtin_provider_names() -> &'static [&'static str] {
829 &Self::BUILTIN_PROVIDER_IDS
830 }
831
832 pub fn get(&self, name: &str) -> Option<&ProviderConfig> {
833 match name {
834 "anthropic" => Some(&self.anthropic),
835 "openai" => Some(&self.openai),
836 "openrouter" => Some(&self.openrouter),
837 "deepseek" => Some(&self.deepseek),
838 "groq" => Some(&self.groq),
839 "zhipu" => Some(&self.zhipu),
840 "dashscope" => Some(&self.dashscope),
841 "vllm" => Some(&self.vllm),
842 "gemini" => Some(&self.gemini),
843 "moonshot" => Some(&self.moonshot),
844 "minimax" => Some(&self.minimax),
845 "aihubmix" => Some(&self.aihubmix),
846 "custom" => Some(&self.custom),
847 _ => None,
848 }
849 }
850
851 pub fn get_mut(&mut self, name: &str) -> Option<&mut ProviderConfig> {
852 match name {
853 "anthropic" => Some(&mut self.anthropic),
854 "openai" => Some(&mut self.openai),
855 "openrouter" => Some(&mut self.openrouter),
856 "deepseek" => Some(&mut self.deepseek),
857 "groq" => Some(&mut self.groq),
858 "zhipu" => Some(&mut self.zhipu),
859 "dashscope" => Some(&mut self.dashscope),
860 "vllm" => Some(&mut self.vllm),
861 "gemini" => Some(&mut self.gemini),
862 "moonshot" => Some(&mut self.moonshot),
863 "minimax" => Some(&mut self.minimax),
864 "aihubmix" => Some(&mut self.aihubmix),
865 "custom" => Some(&mut self.custom),
866 _ => None,
867 }
868 }
869
870 pub fn get_custom(&self, name: &str) -> Option<&CustomProviderConfig> {
871 self.custom_providers.get(name)
872 }
873
874 pub fn get_custom_mut(&mut self, name: &str) -> Option<&mut CustomProviderConfig> {
875 self.custom_providers.get_mut(name)
876 }
877
878 pub fn is_builtin_provider(name: &str) -> bool {
879 Self::BUILTIN_PROVIDER_IDS.contains(&name)
880 }
881}
882
883#[derive(Debug, Clone, Serialize, Deserialize)]
885pub struct GatewayConfig {
886 #[serde(default = "default_host")]
887 pub host: String,
888 #[serde(default = "default_port")]
889 pub port: u16,
890}
891
892fn default_host() -> String {
893 "0.0.0.0".to_string()
894}
895fn default_port() -> u16 {
896 3000
897}
898
899impl Default for GatewayConfig {
900 fn default() -> Self {
901 Self {
902 host: default_host(),
903 port: default_port(),
904 }
905 }
906}
907
908#[derive(Debug, Clone, Serialize, Deserialize, Default)]
910pub struct ToolsConfig {
911 #[serde(default)]
912 pub builtin: BuiltInToolsConfig,
913 #[serde(default)]
914 pub web: WebToolsConfig,
915 #[serde(default)]
916 pub exec: ExecToolConfig,
917 #[serde(default)]
918 pub restrict_to_workspace: bool,
919 #[serde(default, rename = "mcpServers", alias = "mcp_servers")]
920 pub mcp_servers: HashMap<String, MCPServerConfig>,
921 #[serde(default, rename = "mcpManager", alias = "mcp_manager")]
922 pub mcp_manager: MCPManagerConfig,
923}
924
925#[derive(Debug, Clone, Serialize, Deserialize)]
926pub struct BuiltInToolsConfig {
927 #[serde(default = "default_enabled")]
928 pub filesystem: bool,
929 #[serde(default = "default_enabled")]
930 pub shell: bool,
931 #[serde(default = "default_enabled")]
932 pub web_search: bool,
933 #[serde(default = "default_enabled")]
934 pub web_fetch: bool,
935 #[serde(default = "default_enabled")]
936 pub spawn: bool,
937 #[serde(default)]
938 pub cron: bool,
939 #[serde(default = "default_enabled")]
940 pub mcp: bool,
941 #[serde(default = "default_enabled")]
942 pub attachment: bool,
943}
944
945impl Default for BuiltInToolsConfig {
946 fn default() -> Self {
947 Self {
948 filesystem: true,
949 shell: true,
950 web_search: true,
951 web_fetch: true,
952 spawn: true,
953 cron: false,
954 mcp: true,
955 attachment: true,
956 }
957 }
958}
959
960#[derive(Debug, Clone, Serialize, Deserialize, Default)]
961pub struct MCPManagerConfig {
962 #[serde(default)]
963 pub disabled_servers: Vec<String>,
964}
965
966impl ToolsConfig {
967 pub fn active_mcp_servers(&self) -> HashMap<String, MCPServerConfig> {
968 self.mcp_servers
969 .iter()
970 .filter(|(name, _)| !self.is_mcp_server_disabled(name))
971 .map(|(name, cfg)| (name.clone(), cfg.clone()))
972 .collect()
973 }
974
975 pub fn is_mcp_server_disabled(&self, name: &str) -> bool {
976 self.mcp_manager
977 .disabled_servers
978 .iter()
979 .any(|server| server == name)
980 }
981}
982
983#[derive(Debug, Clone, Serialize, Deserialize, Default)]
985pub struct MCPServerConfig {
986 #[serde(default)]
987 pub command: String,
988 #[serde(default)]
989 pub args: Vec<String>,
990 #[serde(default)]
991 pub env: HashMap<String, String>,
992 #[serde(default)]
993 pub url: String,
994 #[serde(default = "default_tool_timeout")]
996 pub tool_timeout: u64,
997}
998
999fn default_tool_timeout() -> u64 {
1000 30
1001}
1002
1003#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1005pub struct WebToolsConfig {
1006 #[serde(default)]
1007 pub search: WebSearchConfig,
1008 #[serde(default)]
1009 pub fetch: WebFetchConfig,
1010}
1011
1012#[derive(Debug, Clone, Serialize, Deserialize)]
1013pub struct WebFetchConfig {
1014 #[serde(default = "default_enabled")]
1015 pub enabled: bool,
1016}
1017
1018#[derive(Debug, Clone, Serialize, Deserialize)]
1020pub struct WebSearchConfig {
1021 #[serde(default = "default_search_provider")]
1022 pub provider: String,
1023 #[serde(default = "default_enabled")]
1024 pub enabled: bool,
1025 #[serde(default)]
1026 pub api_key: String,
1027 #[serde(default = "default_max_results")]
1028 pub max_results: u32,
1029}
1030
1031fn default_search_provider() -> String {
1032 "bocha".to_string()
1033}
1034
1035fn default_enabled() -> bool {
1036 true
1037}
1038
1039fn default_max_results() -> u32 {
1040 5
1041}
1042
1043impl Default for WebSearchConfig {
1044 fn default() -> Self {
1045 Self {
1046 provider: default_search_provider(),
1047 enabled: default_enabled(),
1048 api_key: String::new(),
1049 max_results: default_max_results(),
1050 }
1051 }
1052}
1053
1054impl Default for WebFetchConfig {
1055 fn default() -> Self {
1056 Self {
1057 enabled: default_enabled(),
1058 }
1059 }
1060}
1061
1062#[derive(Debug, Clone, Serialize, Deserialize)]
1064pub struct ExecToolConfig {
1065 #[serde(default = "default_timeout")]
1066 pub timeout: u64,
1067}
1068
1069fn default_timeout() -> u64 {
1070 60
1071}
1072
1073impl Default for ExecToolConfig {
1074 fn default() -> Self {
1075 Self {
1076 timeout: default_timeout(),
1077 }
1078 }
1079}