1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::config::constants::{KEEP_N_MEESAGES, MAX_CONTEXT_LENGTH, MAX_CONVO_LENGTH};
6use crate::models::BackendConnection;
7
8#[allow(unused_imports)]
9use super::CONFIG;
10use super::model_filter::ModelFilter;
11
12use super::constants::{
13 HELLO_MESSAGE, LOG_FILE_PATH, MAX_BUBBLE_WIDTH_PERCENT, MIN_BUBBLE_WIDTH_PERCENT,
14};
15use super::defaults::*;
16
17#[derive(Deserialize, Serialize, Debug, Clone, Default)]
18pub struct Configuration {
19 #[serde(default)]
20 pub general: GeneralConfig,
21
22 #[serde(default)]
23 pub log: LogConfig,
24
25 #[serde(default)]
26 pub theme: ThemeConfig,
27
28 #[serde(default)]
29 pub backend: BackendConfig,
30
31 #[serde(default)]
32 pub storage: StorageConfig,
33
34 #[serde(default)]
35 pub context: ContextConfig,
36}
37
38#[derive(Deserialize, Serialize, Debug, Clone)]
39pub struct GeneralConfig {
40 #[serde(default = "hello_message")]
41 pub hello_message: Option<String>,
42
43 #[serde(default)]
44 pub show_usage: Option<bool>,
45
46 #[serde(default)]
47 pub verbose: bool,
48
49 #[serde(default = "bubble_width_percent")]
50 pub bubble_width_percent: usize,
51
52 #[serde(default)]
53 pub auto_start: Option<bool>,
54}
55
56#[derive(Default, Deserialize, Serialize, Debug, Clone)]
57pub struct ContextConfig {
58 #[serde(default)]
59 pub compression: ContextCompression,
60
61 #[serde(default)]
62 pub truncation: TokenTruncation,
63}
64
65#[derive(Deserialize, Serialize, Debug, Clone)]
66pub struct ContextCompression {
67 #[serde(default)]
68 pub enabled: bool,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub compress_model: Option<String>,
72
73 #[serde(default = "max_context_length")]
74 pub max_tokens: usize,
75
76 #[serde(default = "max_convo_length")]
77 pub max_messages: usize,
78
79 #[serde(default = "keep_n_messages")]
80 pub keep_n_messages: usize,
81}
82
83#[derive(Deserialize, Serialize, Debug, Clone)]
84pub struct TokenTruncation {
85 #[serde(default)]
86 pub enabled: bool,
87
88 #[serde(default = "max_context_length")]
89 pub max_tokens: usize,
90}
91
92#[derive(Deserialize, Serialize, Debug, Clone)]
93pub struct LogConfig {
94 #[serde(default = "log_level")]
95 pub level: Option<String>,
96
97 #[serde(default)]
98 pub filters: Option<Vec<LogFilter>>,
99
100 #[serde(default)]
101 pub file: LogFile,
102}
103
104#[derive(Deserialize, Serialize, Debug, Clone)]
105pub struct LogFilter {
106 #[serde(default)]
107 pub module: Option<String>,
108
109 #[serde(default)]
110 pub level: Option<String>,
111}
112
113#[derive(Deserialize, Serialize, Debug, Clone)]
114pub struct LogFile {
115 #[serde(default = "log_file_path")]
116 pub path: String,
117
118 #[serde(default)]
119 pub append: bool,
120}
121
122#[derive(Deserialize, Serialize, Debug, Clone)]
123pub struct ThemeConfig {
124 #[serde(default)]
125 pub name: Option<String>,
126
127 #[serde(default)]
128 pub folder_path: Option<String>,
129}
130
131#[derive(Default, Deserialize, Serialize, Debug, Clone)]
132pub struct BackendConfig {
133 #[serde(default)]
134 pub default_model: Option<String>,
135
136 #[serde(default)]
137 pub timeout_secs: Option<u16>,
138
139 #[serde(default)]
140 pub connections: Vec<BackendConnection>,
141
142 #[serde(default)]
143 pub mcp: McpConfig,
144
145 #[serde(default)]
146 pub model_settings: Vec<ModelSetting>,
147}
148
149#[derive(Default, Deserialize, Serialize, Debug, Clone)]
150pub struct McpConfig {
151 #[serde(default)]
152 pub notice_on_call_tool: Option<bool>,
153
154 #[serde(default)]
155 pub servers: Vec<McpServerConfig>,
156}
157
158#[derive(Deserialize, Serialize, Debug, Clone)]
159pub struct ModelSetting {
160 pub model: ModelFilter,
161 #[serde(default = "default_option_true")]
162 pub enable_mcp: Option<bool>,
163}
164
165#[derive(Deserialize, Serialize, Debug, Clone)]
166pub enum McpServer {
167 #[serde(rename = "binary")]
168 Binary(BinaryConfig),
169 #[serde(rename = "websocket")]
170 WebSocket(WebSocketConfig),
171}
172
173#[derive(Deserialize, Serialize, Debug, Clone)]
174pub struct McpServerConfig {
175 #[serde(default = "default_option_true")]
176 pub enabled: Option<bool>,
177 pub provider: String,
178 #[serde(flatten)]
179 pub server: McpServer,
180}
181
182#[derive(Deserialize, Serialize, Debug, Clone)]
183pub struct BinaryConfig {
184 pub filename: String,
185
186 #[serde(default)]
187 pub args: Vec<String>,
188
189 #[serde(default)]
190 pub env: HashMap<String, String>,
191}
192
193#[derive(Deserialize, Serialize, Debug, Clone)]
194pub struct WebSocketConfig {
195 pub url: String,
196}
197
198#[derive(Deserialize, Serialize, Debug, Clone)]
199pub enum StorageConfig {
200 #[serde(rename = "sqlite")]
201 Sqlite(SqliteStorage),
202}
203
204#[derive(Default, Deserialize, Serialize, Debug, Clone)]
205pub struct SqliteStorage {
206 pub path: Option<String>,
207}
208
209impl GeneralConfig {
210 pub fn get_bubble_width_percent(&self) -> usize {
211 let percent = self.bubble_width_percent.min(MAX_BUBBLE_WIDTH_PERCENT);
212 percent.max(MIN_BUBBLE_WIDTH_PERCENT)
213 }
214}
215
216impl Default for LogConfig {
217 fn default() -> Self {
218 Self {
219 level: Some("info".to_string()),
220 file: LogFile::default(),
221 filters: None,
222 }
223 }
224}
225
226impl Default for LogFile {
227 fn default() -> Self {
228 Self {
229 path: LOG_FILE_PATH.to_string(),
230 append: false,
231 }
232 }
233}
234
235impl Default for ThemeConfig {
236 fn default() -> Self {
237 Self {
238 name: Some("base16-ocean.dark".to_string()),
239 folder_path: None,
240 }
241 }
242}
243
244impl Default for ContextCompression {
245 fn default() -> Self {
246 Self {
247 enabled: false,
248 compress_model: None,
249 max_tokens: MAX_CONTEXT_LENGTH,
250 max_messages: MAX_CONVO_LENGTH,
251 keep_n_messages: KEEP_N_MEESAGES,
252 }
253 }
254}
255
256impl Default for TokenTruncation {
257 fn default() -> Self {
258 Self {
259 enabled: false,
260 max_tokens: MAX_CONTEXT_LENGTH,
261 }
262 }
263}
264
265impl Default for GeneralConfig {
266 fn default() -> Self {
267 Self {
268 verbose: false,
269 hello_message: Some(HELLO_MESSAGE.to_string()),
270 show_usage: None,
271 bubble_width_percent: 80,
272 auto_start: None,
273 }
274 }
275}
276
277impl Default for StorageConfig {
278 fn default() -> Self {
279 Self::Sqlite(SqliteStorage::default())
280 }
281}