mcp_langbase_reasoning/server/
mod.rs1mod handlers;
10mod mcp;
11
12pub use handlers::*;
13pub use mcp::*;
14
15use std::sync::Arc;
16
17use crate::config::Config;
18use crate::langbase::LangbaseClient;
19use crate::modes::{
20 AutoMode, BacktrackingMode, DecisionMode, DetectionMode, DivergentMode, EvidenceMode, GotMode,
21 LinearMode, ReflectionMode, TreeMode,
22};
23use crate::presets::PresetRegistry;
24use crate::self_improvement::{SelfImprovementConfig, SelfImprovementSystem};
25use crate::storage::SqliteStorage;
26
27pub struct AppState {
32 pub config: Config,
34 pub storage: SqliteStorage,
36 pub langbase: LangbaseClient,
38 pub linear_mode: LinearMode,
40 pub tree_mode: TreeMode,
42 pub divergent_mode: DivergentMode,
44 pub reflection_mode: ReflectionMode,
46 pub backtracking_mode: BacktrackingMode,
48 pub auto_mode: AutoMode,
50 pub got_mode: GotMode,
52 pub decision_mode: DecisionMode,
54 pub evidence_mode: EvidenceMode,
56 pub detection_mode: DetectionMode,
58 pub preset_registry: Arc<PresetRegistry>,
60 pub self_improvement: Option<Arc<SelfImprovementSystem>>,
65}
66
67impl AppState {
68 pub fn new(config: Config, storage: SqliteStorage, langbase: LangbaseClient) -> Self {
70 tracing::info!(
72 detection_pipe = ?config.pipes.detection.as_ref().and_then(|d| d.pipe.as_ref()),
73 decision_pipe = ?config.pipes.decision.as_ref().and_then(|d| d.pipe.as_ref()),
74 linear_pipe = %config.pipes.linear,
75 "AppState initializing with pipe configuration"
76 );
77
78 let linear_mode = LinearMode::new(storage.clone(), langbase.clone(), &config);
79 let tree_mode = TreeMode::new(storage.clone(), langbase.clone(), &config);
80 let divergent_mode = DivergentMode::new(storage.clone(), langbase.clone(), &config);
81 let reflection_mode = ReflectionMode::new(storage.clone(), langbase.clone(), &config);
82 let backtracking_mode = BacktrackingMode::new(storage.clone(), langbase.clone(), &config);
83 let auto_mode = AutoMode::new(storage.clone(), langbase.clone(), &config);
84 let got_mode = GotMode::new(storage.clone(), langbase.clone(), &config);
85 let decision_mode = DecisionMode::new(storage.clone(), langbase.clone(), &config);
86 let evidence_mode = EvidenceMode::new(storage.clone(), langbase.clone(), &config);
87 let detection_mode = DetectionMode::new(storage.clone(), langbase.clone(), &config);
88 let preset_registry = Arc::new(PresetRegistry::new());
89
90 let self_improvement_config = SelfImprovementConfig::from_env();
92 let self_improvement = if self_improvement_config.enabled {
93 tracing::info!("Self-improvement system enabled (autonomous optimization active)");
94 Some(Arc::new(SelfImprovementSystem::new(
95 self_improvement_config,
96 storage.clone(),
97 langbase.clone(),
98 )))
99 } else {
100 tracing::warn!("Self-improvement system disabled via SELF_IMPROVEMENT_ENABLED=false");
101 None
102 };
103
104 Self {
105 config,
106 storage,
107 langbase,
108 linear_mode,
109 tree_mode,
110 divergent_mode,
111 reflection_mode,
112 backtracking_mode,
113 auto_mode,
114 got_mode,
115 decision_mode,
116 evidence_mode,
117 detection_mode,
118 preset_registry,
119 self_improvement,
120 }
121 }
122
123 pub async fn record_invocation(&self, event: crate::self_improvement::InvocationEvent) {
128 if let Some(ref system) = self.self_improvement {
129 system.on_invocation(event).await;
130 }
131 }
132
133 pub fn self_improvement_enabled(&self) -> bool {
135 self.self_improvement.is_some()
136 }
137}
138
139impl Clone for AppState {
141 fn clone(&self) -> Self {
142 Self {
143 config: self.config.clone(),
144 storage: self.storage.clone(),
145 langbase: self.langbase.clone(),
146 linear_mode: self.linear_mode.clone(),
147 tree_mode: self.tree_mode.clone(),
148 divergent_mode: self.divergent_mode.clone(),
149 reflection_mode: self.reflection_mode.clone(),
150 backtracking_mode: self.backtracking_mode.clone(),
151 auto_mode: self.auto_mode.clone(),
152 got_mode: self.got_mode.clone(),
153 decision_mode: self.decision_mode.clone(),
154 evidence_mode: self.evidence_mode.clone(),
155 detection_mode: self.detection_mode.clone(),
156 preset_registry: Arc::clone(&self.preset_registry),
157 self_improvement: self.self_improvement.as_ref().map(Arc::clone),
158 }
159 }
160}
161
162pub type SharedState = Arc<AppState>;
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::config::{
169 DatabaseConfig, ErrorHandlingConfig, LangbaseConfig, LogFormat, LoggingConfig, PipeConfig,
170 RequestConfig,
171 };
172 use std::path::PathBuf;
173
174 fn create_test_config() -> Config {
175 Config {
176 langbase: LangbaseConfig {
177 api_key: "test-key".to_string(),
178 base_url: "https://api.langbase.com".to_string(),
179 },
180 database: DatabaseConfig {
181 path: PathBuf::from(":memory:"),
182 max_connections: 5,
183 },
184 logging: LoggingConfig {
185 level: "info".to_string(),
186 format: LogFormat::Pretty,
187 },
188 request: RequestConfig::default(),
189 pipes: PipeConfig::default(),
190 error_handling: ErrorHandlingConfig::default(),
191 }
192 }
193
194 #[tokio::test]
195 async fn test_app_state_new() {
196 let config = create_test_config();
197 let storage = SqliteStorage::new_in_memory().await.unwrap();
198 let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
199
200 let state = AppState::new(config.clone(), storage, langbase);
201
202 assert_eq!(state.config.langbase.api_key, "test-key");
204 }
205
206 #[tokio::test]
207 async fn test_app_state_clone() {
208 let config = create_test_config();
209 let storage = SqliteStorage::new_in_memory().await.unwrap();
210 let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
211
212 let state1 = AppState::new(config, storage, langbase);
213 let state2 = state1.clone();
214
215 assert_eq!(
216 state1.config.langbase.api_key,
217 state2.config.langbase.api_key
218 );
219 }
220
221 #[tokio::test]
222 async fn test_shared_state_type() {
223 let config = create_test_config();
224 let storage = SqliteStorage::new_in_memory().await.unwrap();
225 let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
226
227 let state = AppState::new(config, storage, langbase);
228 let shared: SharedState = Arc::new(state);
229
230 let shared2 = Arc::clone(&shared);
232 assert_eq!(Arc::strong_count(&shared), 2);
233 drop(shared2);
234 assert_eq!(Arc::strong_count(&shared), 1);
235 }
236
237 #[tokio::test]
238 async fn test_app_state_has_all_modes() {
239 let config = create_test_config();
240 let storage = SqliteStorage::new_in_memory().await.unwrap();
241 let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
242
243 let state = AppState::new(config, storage, langbase);
244
245 assert!(state.preset_registry.count() >= 5);
247 }
248
249 #[tokio::test]
250 async fn test_app_state_storage_access() {
251 use crate::storage::Storage;
252
253 let config = create_test_config();
254 let storage = SqliteStorage::new_in_memory().await.unwrap();
255 let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
256
257 let state = AppState::new(config, storage.clone(), langbase);
258
259 let session = crate::storage::Session::new("test-metadata");
261 state.storage.create_session(&session).await.unwrap();
262 let retrieved = state.storage.get_session(&session.id).await.unwrap();
263 assert!(retrieved.is_some());
264 }
265
266 #[tokio::test]
267 async fn test_app_state_config_access() {
268 let config = create_test_config();
269 let storage = SqliteStorage::new_in_memory().await.unwrap();
270 let langbase = LangbaseClient::new(&config.langbase, config.request.clone()).unwrap();
271
272 let state = AppState::new(config.clone(), storage, langbase);
273
274 assert_eq!(state.config.langbase.base_url, "https://api.langbase.com");
276 assert_eq!(state.config.database.max_connections, 5);
277 assert_eq!(state.config.logging.level, "info");
278 }
279}