1use serde::{Deserialize, Serialize};
10use std::time::Instant;
11use tracing::{debug, info, warn};
12
13use super::{extract_json_from_completion, serialize_for_log, ModeCore};
14use crate::config::Config;
15use crate::error::{AppResult, ToolError};
16use crate::langbase::{LangbaseClient, Message, PipeRequest};
17use crate::prompts::REFLECTION_PROMPT;
18use crate::storage::{Invocation, SqliteStorage, Storage, Thought};
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ReflectionParams {
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub thought_id: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub content: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub session_id: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub branch_id: Option<String>,
35 #[serde(default = "default_max_iterations")]
37 pub max_iterations: usize,
38 #[serde(default = "default_quality_threshold")]
40 pub quality_threshold: f64,
41 #[serde(default)]
43 pub include_chain: bool,
44}
45
46fn default_max_iterations() -> usize {
47 3
48}
49
50fn default_quality_threshold() -> f64 {
51 0.8
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct ReflectionResponse {
57 pub analysis: String,
59 pub strengths: Vec<String>,
61 pub weaknesses: Vec<String>,
63 pub recommendations: Vec<String>,
65 pub confidence: f64,
67 #[serde(default)]
69 pub quality_score: Option<f64>,
70 #[serde(default)]
72 pub improved_thought: Option<String>,
73 #[serde(default)]
75 pub metadata: serde_json::Value,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct ReflectionResult {
81 pub session_id: String,
83 pub reflection_thought_id: String,
85 pub original_thought_id: Option<String>,
87 pub analysis: String,
89 pub strengths: Vec<String>,
91 pub weaknesses: Vec<String>,
93 pub recommendations: Vec<String>,
95 pub quality_score: f64,
97 pub improved_thought: Option<ImprovedThought>,
99 pub iterations_performed: usize,
101 pub quality_improved: bool,
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub branch_id: Option<String>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ImprovedThought {
111 pub thought_id: String,
113 pub content: String,
115 pub confidence: f64,
117}
118
119#[derive(Clone)]
121pub struct ReflectionMode {
122 core: ModeCore,
124 pipe_name: String,
126}
127
128impl ReflectionMode {
129 pub fn new(storage: SqliteStorage, langbase: LangbaseClient, config: &Config) -> Self {
131 Self {
132 core: ModeCore::new(storage, langbase),
133 pipe_name: config.pipes.reflection.clone(),
134 }
135 }
136
137 pub async fn process(&self, params: ReflectionParams) -> AppResult<ReflectionResult> {
139 let start = Instant::now();
140
141 if params.thought_id.is_none() && params.content.is_none() {
143 return Err(ToolError::Validation {
144 field: "thought_id or content".to_string(),
145 reason: "Either thought_id or content must be provided".to_string(),
146 }
147 .into());
148 }
149
150 let session = self
152 .core
153 .storage()
154 .get_or_create_session(¶ms.session_id, "reflection")
155 .await?;
156 debug!(session_id = %session.id, "Processing reflection reasoning");
157
158 let (original_content, original_thought) = if let Some(thought_id) = ¶ms.thought_id {
160 let thought = self
161 .core
162 .storage()
163 .get_thought(thought_id)
164 .await?
165 .ok_or_else(|| ToolError::Session(format!("Thought not found: {}", thought_id)))?;
166 (thought.content.clone(), Some(thought))
167 } else {
168 (params.content.clone().unwrap_or_default(), None)
169 };
170
171 let context_chain = if params.include_chain {
173 if let Some(ref thought) = original_thought {
174 self.get_reasoning_chain(&session.id, thought).await?
175 } else {
176 Vec::new()
177 }
178 } else {
179 Vec::new()
180 };
181
182 let max_iterations = params.max_iterations.min(5);
184 let mut current_content = original_content.clone();
185 let mut iterations_performed = 0;
186 let mut best_quality = 0.0;
187 let mut final_response: Option<ReflectionResponse> = None;
188
189 for iteration in 0..max_iterations {
190 iterations_performed = iteration + 1;
191
192 let messages = self.build_messages(¤t_content, &context_chain, iteration);
194
195 let mut invocation = Invocation::new(
197 "reasoning.reflection",
198 serde_json::json!({
199 "iteration": iteration,
200 "content": ¤t_content
201 }),
202 )
203 .with_session(&session.id)
204 .with_pipe(&self.pipe_name);
205
206 let request = PipeRequest::new(&self.pipe_name, messages);
208 let response = match self.core.langbase().call_pipe(request).await {
209 Ok(resp) => resp,
210 Err(e) => {
211 let latency = start.elapsed().as_millis() as i64;
212 invocation = invocation.failure(e.to_string(), latency);
213 self.core.storage().log_invocation(&invocation).await?;
214 return Err(e.into());
215 }
216 };
217
218 let reflection = self.parse_response(&response.completion)?;
220 let quality = reflection.quality_score.unwrap_or(reflection.confidence);
221
222 let latency = start.elapsed().as_millis() as i64;
224 invocation = invocation.success(
225 serialize_for_log(&reflection, "reasoning.reflection output"),
226 latency,
227 );
228 self.core.storage().log_invocation(&invocation).await?;
229
230 if quality >= params.quality_threshold {
232 debug!(
233 iteration = iteration,
234 quality = quality,
235 threshold = params.quality_threshold,
236 "Quality threshold met, stopping iterations"
237 );
238 best_quality = quality;
239 final_response = Some(reflection);
240 break;
241 }
242
243 if let Some(ref improved) = reflection.improved_thought {
245 if quality > best_quality {
246 best_quality = quality;
247 current_content = improved.clone();
248 }
249 }
250
251 final_response = Some(reflection);
252 }
253
254 let reflection = final_response.ok_or_else(|| ToolError::Reasoning {
255 message: "No reflection response generated".to_string(),
256 })?;
257
258 let reflection_thought = Thought::new(&session.id, &reflection.analysis, "reflection")
260 .with_confidence(reflection.confidence)
261 .with_metadata(serde_json::json!({
262 "strengths": reflection.strengths,
263 "weaknesses": reflection.weaknesses,
264 "recommendations": reflection.recommendations,
265 "quality_score": best_quality,
266 "iterations": iterations_performed
267 }));
268
269 let reflection_thought = if let Some(ref thought) = original_thought {
270 reflection_thought.with_parent(&thought.id)
271 } else {
272 reflection_thought
273 };
274
275 let reflection_thought = if let Some(ref branch_id) = params.branch_id {
276 reflection_thought.with_branch(branch_id)
277 } else {
278 reflection_thought
279 };
280
281 self.core
282 .storage()
283 .create_thought(&reflection_thought)
284 .await?;
285
286 let improved_thought = if let Some(ref improved_content) = reflection.improved_thought {
288 if improved_content != &original_content {
289 let improved = Thought::new(&session.id, improved_content, "reflection")
290 .with_confidence(best_quality)
291 .with_parent(&reflection_thought.id)
292 .with_metadata(serde_json::json!({
293 "is_improved_version": true,
294 "original_thought_id": original_thought.as_ref().map(|t| &t.id)
295 }));
296
297 let improved = if let Some(ref branch_id) = params.branch_id {
298 improved.with_branch(branch_id)
299 } else {
300 improved
301 };
302
303 self.core.storage().create_thought(&improved).await?;
304
305 Some(ImprovedThought {
306 thought_id: improved.id,
307 content: improved_content.clone(),
308 confidence: best_quality,
309 })
310 } else {
311 None
312 }
313 } else {
314 None
315 };
316
317 let quality_improved = best_quality
318 > original_thought
319 .as_ref()
320 .map(|t| t.confidence)
321 .unwrap_or(0.5);
322
323 info!(
324 session_id = %session.id,
325 reflection_id = %reflection_thought.id,
326 iterations = iterations_performed,
327 quality_score = best_quality,
328 quality_improved = quality_improved,
329 latency_ms = start.elapsed().as_millis(),
330 "Reflection reasoning completed"
331 );
332
333 Ok(ReflectionResult {
334 session_id: session.id,
335 reflection_thought_id: reflection_thought.id,
336 original_thought_id: original_thought.map(|t| t.id),
337 analysis: reflection.analysis,
338 strengths: reflection.strengths,
339 weaknesses: reflection.weaknesses,
340 recommendations: reflection.recommendations,
341 quality_score: best_quality,
342 improved_thought,
343 iterations_performed,
344 quality_improved,
345 branch_id: params.branch_id,
346 })
347 }
348
349 pub async fn evaluate_session(&self, session_id: &str) -> AppResult<SessionEvaluation> {
351 let thoughts = self.core.storage().get_session_thoughts(session_id).await?;
352
353 if thoughts.is_empty() {
354 return Err(
355 ToolError::Session("Session has no thoughts to evaluate".to_string()).into(),
356 );
357 }
358
359 let total_confidence: f64 = thoughts.iter().map(|t| t.confidence).sum();
360 let avg_confidence = total_confidence / thoughts.len() as f64;
361
362 let mode_counts: std::collections::HashMap<String, usize> =
363 thoughts
364 .iter()
365 .fold(std::collections::HashMap::new(), |mut acc, t| {
366 *acc.entry(t.mode.clone()).or_insert(0) += 1;
367 acc
368 });
369
370 let coherence_score = self.calculate_coherence(&thoughts);
371
372 Ok(SessionEvaluation {
373 session_id: session_id.to_string(),
374 total_thoughts: thoughts.len(),
375 average_confidence: avg_confidence,
376 mode_distribution: mode_counts,
377 coherence_score,
378 recommendation: if avg_confidence < 0.6 {
379 "Consider reviewing and refining low-confidence thoughts".to_string()
380 } else if coherence_score < 0.5 {
381 "Reasoning chain may have logical gaps - consider reflection mode".to_string()
382 } else {
383 "Reasoning quality is acceptable".to_string()
384 },
385 })
386 }
387
388 async fn get_reasoning_chain(
389 &self,
390 session_id: &str,
391 thought: &Thought,
392 ) -> AppResult<Vec<Thought>> {
393 let all_thoughts = self.core.storage().get_session_thoughts(session_id).await?;
394
395 let mut chain = Vec::new();
397 let mut current_id = thought.parent_id.clone();
398
399 while let Some(parent_id) = current_id {
400 if let Some(parent) = all_thoughts.iter().find(|t| t.id == parent_id) {
401 chain.push(parent.clone());
402 current_id = parent.parent_id.clone();
403 } else {
404 break;
405 }
406
407 if chain.len() >= 10 {
409 break;
410 }
411 }
412
413 chain.reverse(); chain.push(thought.clone()); Ok(chain)
417 }
418
419 fn calculate_coherence(&self, thoughts: &[Thought]) -> f64 {
420 if thoughts.len() < 2 {
421 return 1.0;
422 }
423
424 let mut linked_count = 0;
426 for thought in thoughts.iter().skip(1) {
427 if thought.parent_id.is_some() {
428 linked_count += 1;
429 }
430 }
431
432 let link_ratio = linked_count as f64 / (thoughts.len() - 1) as f64;
433
434 let confidence_stability: f64 = thoughts
436 .windows(2)
437 .map(|w| 1.0 - (w[0].confidence - w[1].confidence).abs())
438 .sum::<f64>()
439 / (thoughts.len() - 1) as f64;
440
441 (link_ratio + confidence_stability) / 2.0
442 }
443
444 fn build_messages(&self, content: &str, chain: &[Thought], iteration: usize) -> Vec<Message> {
445 let mut messages = Vec::new();
446
447 let mut system_prompt = REFLECTION_PROMPT.to_string();
449 if iteration > 0 {
450 system_prompt.push_str(&format!(
451 "\n\nThis is iteration {} of reflection. Focus on addressing previously identified weaknesses and improving the thought quality.",
452 iteration + 1
453 ));
454 }
455
456 messages.push(Message::system(system_prompt));
457
458 if !chain.is_empty() {
460 let chain_text: Vec<String> = chain
461 .iter()
462 .map(|t| {
463 format!(
464 "- [{}] (confidence: {:.2}) {}",
465 t.mode, t.confidence, t.content
466 )
467 })
468 .collect();
469
470 messages.push(Message::user(format!(
471 "Reasoning chain leading to this thought:\n{}\n\nNow reflect on the final thought:",
472 chain_text.join("\n")
473 )));
474 }
475
476 messages.push(Message::user(format!(
478 "Thought to reflect upon:\n\n{}",
479 content
480 )));
481
482 messages
483 }
484
485 fn parse_response(&self, completion: &str) -> AppResult<ReflectionResponse> {
486 let json_str = extract_json_from_completion(completion).map_err(|e| {
487 warn!(
488 error = %e,
489 completion_preview = %completion.chars().take(200).collect::<String>(),
490 "Failed to extract JSON from reflection response"
491 );
492 ToolError::Reasoning {
493 message: format!("Reflection response extraction failed: {}", e),
494 }
495 })?;
496
497 serde_json::from_str::<ReflectionResponse>(json_str).map_err(|e| {
498 ToolError::Reasoning {
499 message: format!("Failed to parse reflection response: {}", e),
500 }
501 .into()
502 })
503 }
504}
505
506#[derive(Debug, Clone, Serialize, Deserialize)]
508pub struct SessionEvaluation {
509 pub session_id: String,
511 pub total_thoughts: usize,
513 pub average_confidence: f64,
515 pub mode_distribution: std::collections::HashMap<String, usize>,
517 pub coherence_score: f64,
519 pub recommendation: String,
521}
522
523impl ReflectionParams {
524 pub fn for_thought(thought_id: impl Into<String>) -> Self {
526 Self {
527 thought_id: Some(thought_id.into()),
528 content: None,
529 session_id: None,
530 branch_id: None,
531 max_iterations: default_max_iterations(),
532 quality_threshold: default_quality_threshold(),
533 include_chain: false,
534 }
535 }
536
537 pub fn for_content(content: impl Into<String>) -> Self {
539 Self {
540 thought_id: None,
541 content: Some(content.into()),
542 session_id: None,
543 branch_id: None,
544 max_iterations: default_max_iterations(),
545 quality_threshold: default_quality_threshold(),
546 include_chain: false,
547 }
548 }
549
550 pub fn with_session(mut self, session_id: impl Into<String>) -> Self {
552 self.session_id = Some(session_id.into());
553 self
554 }
555
556 pub fn with_branch(mut self, branch_id: impl Into<String>) -> Self {
558 self.branch_id = Some(branch_id.into());
559 self
560 }
561
562 pub fn with_max_iterations(mut self, max: usize) -> Self {
564 self.max_iterations = max.clamp(1, 5);
565 self
566 }
567
568 pub fn with_quality_threshold(mut self, threshold: f64) -> Self {
570 self.quality_threshold = threshold.clamp(0.0, 1.0);
571 self
572 }
573
574 pub fn with_chain(mut self) -> Self {
576 self.include_chain = true;
577 self
578 }
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584
585 #[test]
590 fn test_default_max_iterations() {
591 assert_eq!(default_max_iterations(), 3);
592 }
593
594 #[test]
595 fn test_default_quality_threshold() {
596 assert_eq!(default_quality_threshold(), 0.8);
597 }
598
599 #[test]
604 fn test_reflection_params_for_thought() {
605 let params = ReflectionParams::for_thought("thought-123");
606 assert_eq!(params.thought_id, Some("thought-123".to_string()));
607 assert!(params.content.is_none());
608 assert!(params.session_id.is_none());
609 assert!(params.branch_id.is_none());
610 assert_eq!(params.max_iterations, 3);
611 assert_eq!(params.quality_threshold, 0.8);
612 assert!(!params.include_chain);
613 }
614
615 #[test]
616 fn test_reflection_params_for_content() {
617 let params = ReflectionParams::for_content("Some content to reflect upon");
618 assert!(params.thought_id.is_none());
619 assert_eq!(
620 params.content,
621 Some("Some content to reflect upon".to_string())
622 );
623 assert!(params.session_id.is_none());
624 assert_eq!(params.max_iterations, 3);
625 }
626
627 #[test]
628 fn test_reflection_params_with_session() {
629 let params = ReflectionParams::for_thought("t-1").with_session("sess-123");
630 assert_eq!(params.session_id, Some("sess-123".to_string()));
631 }
632
633 #[test]
634 fn test_reflection_params_with_branch() {
635 let params = ReflectionParams::for_thought("t-1").with_branch("branch-456");
636 assert_eq!(params.branch_id, Some("branch-456".to_string()));
637 }
638
639 #[test]
640 fn test_reflection_params_with_max_iterations() {
641 let params = ReflectionParams::for_thought("t-1").with_max_iterations(4);
642 assert_eq!(params.max_iterations, 4);
643 }
644
645 #[test]
646 fn test_reflection_params_max_iterations_clamped_high() {
647 let params = ReflectionParams::for_thought("t-1").with_max_iterations(10);
648 assert_eq!(params.max_iterations, 5); }
650
651 #[test]
652 fn test_reflection_params_max_iterations_clamped_low() {
653 let params = ReflectionParams::for_thought("t-1").with_max_iterations(0);
654 assert_eq!(params.max_iterations, 1); }
656
657 #[test]
658 fn test_reflection_params_with_quality_threshold() {
659 let params = ReflectionParams::for_thought("t-1").with_quality_threshold(0.9);
660 assert_eq!(params.quality_threshold, 0.9);
661 }
662
663 #[test]
664 fn test_reflection_params_quality_threshold_clamped_high() {
665 let params = ReflectionParams::for_thought("t-1").with_quality_threshold(1.5);
666 assert_eq!(params.quality_threshold, 1.0);
667 }
668
669 #[test]
670 fn test_reflection_params_quality_threshold_clamped_low() {
671 let params = ReflectionParams::for_thought("t-1").with_quality_threshold(-0.5);
672 assert_eq!(params.quality_threshold, 0.0);
673 }
674
675 #[test]
676 fn test_reflection_params_with_chain() {
677 let params = ReflectionParams::for_thought("t-1").with_chain();
678 assert!(params.include_chain);
679 }
680
681 #[test]
682 fn test_reflection_params_builder_chain() {
683 let params = ReflectionParams::for_thought("t-1")
684 .with_session("my-session")
685 .with_branch("my-branch")
686 .with_max_iterations(4)
687 .with_quality_threshold(0.85)
688 .with_chain();
689
690 assert_eq!(params.thought_id, Some("t-1".to_string()));
691 assert_eq!(params.session_id, Some("my-session".to_string()));
692 assert_eq!(params.branch_id, Some("my-branch".to_string()));
693 assert_eq!(params.max_iterations, 4);
694 assert_eq!(params.quality_threshold, 0.85);
695 assert!(params.include_chain);
696 }
697
698 #[test]
699 fn test_reflection_params_serialize() {
700 let params = ReflectionParams::for_thought("t-1")
701 .with_session("sess-1")
702 .with_max_iterations(3);
703
704 let json = serde_json::to_string(¶ms).unwrap();
705 assert!(json.contains("t-1"));
706 assert!(json.contains("sess-1"));
707 assert!(json.contains("\"max_iterations\":3"));
708 }
709
710 #[test]
711 fn test_reflection_params_deserialize() {
712 let json = r#"{
713 "thought_id": "t-1",
714 "content": "Some content",
715 "session_id": "s-1",
716 "branch_id": "b-1",
717 "max_iterations": 4,
718 "quality_threshold": 0.9,
719 "include_chain": true
720 }"#;
721 let params: ReflectionParams = serde_json::from_str(json).unwrap();
722
723 assert_eq!(params.thought_id, Some("t-1".to_string()));
724 assert_eq!(params.content, Some("Some content".to_string()));
725 assert_eq!(params.session_id, Some("s-1".to_string()));
726 assert_eq!(params.branch_id, Some("b-1".to_string()));
727 assert_eq!(params.max_iterations, 4);
728 assert_eq!(params.quality_threshold, 0.9);
729 assert!(params.include_chain);
730 }
731
732 #[test]
733 fn test_reflection_params_deserialize_minimal() {
734 let json = r#"{"thought_id": "t-1"}"#;
735 let params: ReflectionParams = serde_json::from_str(json).unwrap();
736
737 assert_eq!(params.thought_id, Some("t-1".to_string()));
738 assert!(params.content.is_none());
739 assert!(params.session_id.is_none());
740 assert_eq!(params.max_iterations, 3); assert_eq!(params.quality_threshold, 0.8); assert!(!params.include_chain); }
744
745 #[test]
750 fn test_reflection_response_serialize() {
751 let response = ReflectionResponse {
752 analysis: "This is the analysis".to_string(),
753 strengths: vec!["Strength 1".to_string(), "Strength 2".to_string()],
754 weaknesses: vec!["Weakness 1".to_string()],
755 recommendations: vec!["Recommendation 1".to_string()],
756 confidence: 0.85,
757 quality_score: Some(0.9),
758 improved_thought: Some("Improved version".to_string()),
759 metadata: serde_json::json!({"key": "value"}),
760 };
761
762 let json = serde_json::to_string(&response).unwrap();
763 assert!(json.contains("This is the analysis"));
764 assert!(json.contains("Strength 1"));
765 assert!(json.contains("Weakness 1"));
766 assert!(json.contains("0.85"));
767 assert!(json.contains("0.9"));
768 assert!(json.contains("Improved version"));
769 }
770
771 #[test]
772 fn test_reflection_response_deserialize() {
773 let json = r#"{
774 "analysis": "Analysis text",
775 "strengths": ["S1", "S2"],
776 "weaknesses": ["W1"],
777 "recommendations": ["R1", "R2"],
778 "confidence": 0.75,
779 "quality_score": 0.8,
780 "improved_thought": "Better thought"
781 }"#;
782 let response: ReflectionResponse = serde_json::from_str(json).unwrap();
783
784 assert_eq!(response.analysis, "Analysis text");
785 assert_eq!(response.strengths.len(), 2);
786 assert_eq!(response.weaknesses.len(), 1);
787 assert_eq!(response.recommendations.len(), 2);
788 assert_eq!(response.confidence, 0.75);
789 assert_eq!(response.quality_score, Some(0.8));
790 assert_eq!(
791 response.improved_thought,
792 Some("Better thought".to_string())
793 );
794 }
795
796 #[test]
797 fn test_reflection_response_deserialize_minimal() {
798 let json = r#"{
799 "analysis": "Basic analysis",
800 "strengths": [],
801 "weaknesses": [],
802 "recommendations": [],
803 "confidence": 0.5
804 }"#;
805 let response: ReflectionResponse = serde_json::from_str(json).unwrap();
806
807 assert_eq!(response.analysis, "Basic analysis");
808 assert!(response.strengths.is_empty());
809 assert!(response.quality_score.is_none());
810 assert!(response.improved_thought.is_none());
811 }
812
813 #[test]
818 fn test_improved_thought_serialize() {
819 let improved = ImprovedThought {
820 thought_id: "t-improved".to_string(),
821 content: "Improved content".to_string(),
822 confidence: 0.92,
823 };
824
825 let json = serde_json::to_string(&improved).unwrap();
826 assert!(json.contains("t-improved"));
827 assert!(json.contains("Improved content"));
828 assert!(json.contains("0.92"));
829 }
830
831 #[test]
832 fn test_improved_thought_deserialize() {
833 let json = r#"{
834 "thought_id": "t-1",
835 "content": "Better version",
836 "confidence": 0.88
837 }"#;
838 let improved: ImprovedThought = serde_json::from_str(json).unwrap();
839
840 assert_eq!(improved.thought_id, "t-1");
841 assert_eq!(improved.content, "Better version");
842 assert_eq!(improved.confidence, 0.88);
843 }
844
845 #[test]
850 fn test_reflection_result_serialize() {
851 let result = ReflectionResult {
852 session_id: "sess-1".to_string(),
853 reflection_thought_id: "t-refl".to_string(),
854 original_thought_id: Some("t-orig".to_string()),
855 analysis: "Analysis of thought".to_string(),
856 strengths: vec!["Strong point".to_string()],
857 weaknesses: vec!["Weak point".to_string()],
858 recommendations: vec!["Suggestion".to_string()],
859 quality_score: 0.85,
860 improved_thought: Some(ImprovedThought {
861 thought_id: "t-improved".to_string(),
862 content: "Better".to_string(),
863 confidence: 0.9,
864 }),
865 iterations_performed: 2,
866 quality_improved: true,
867 branch_id: Some("branch-1".to_string()),
868 };
869
870 let json = serde_json::to_string(&result).unwrap();
871 assert!(json.contains("sess-1"));
872 assert!(json.contains("t-refl"));
873 assert!(json.contains("t-orig"));
874 assert!(json.contains("Strong point"));
875 assert!(json.contains("branch-1"));
876 }
877
878 #[test]
879 fn test_reflection_result_deserialize() {
880 let json = r#"{
881 "session_id": "s-1",
882 "reflection_thought_id": "t-r",
883 "original_thought_id": "t-o",
884 "analysis": "Analysis",
885 "strengths": ["S"],
886 "weaknesses": ["W"],
887 "recommendations": ["R"],
888 "quality_score": 0.8,
889 "improved_thought": null,
890 "iterations_performed": 3,
891 "quality_improved": false
892 }"#;
893 let result: ReflectionResult = serde_json::from_str(json).unwrap();
894
895 assert_eq!(result.session_id, "s-1");
896 assert_eq!(result.reflection_thought_id, "t-r");
897 assert_eq!(result.original_thought_id, Some("t-o".to_string()));
898 assert_eq!(result.quality_score, 0.8);
899 assert!(result.improved_thought.is_none());
900 assert_eq!(result.iterations_performed, 3);
901 assert!(!result.quality_improved);
902 assert!(result.branch_id.is_none());
903 }
904
905 #[test]
906 fn test_reflection_result_without_branch() {
907 let result = ReflectionResult {
908 session_id: "s-1".to_string(),
909 reflection_thought_id: "t-1".to_string(),
910 original_thought_id: None,
911 analysis: "No branch".to_string(),
912 strengths: vec![],
913 weaknesses: vec![],
914 recommendations: vec![],
915 quality_score: 0.5,
916 improved_thought: None,
917 iterations_performed: 1,
918 quality_improved: false,
919 branch_id: None,
920 };
921
922 let json = serde_json::to_string(&result).unwrap();
923 assert!(!json.contains("branch_id"));
925 }
926
927 #[test]
932 fn test_session_evaluation_serialize() {
933 let mut mode_dist = std::collections::HashMap::new();
934 mode_dist.insert("linear".to_string(), 5);
935 mode_dist.insert("tree".to_string(), 3);
936
937 let eval = SessionEvaluation {
938 session_id: "sess-eval".to_string(),
939 total_thoughts: 8,
940 average_confidence: 0.75,
941 mode_distribution: mode_dist,
942 coherence_score: 0.82,
943 recommendation: "Reasoning quality is acceptable".to_string(),
944 };
945
946 let json = serde_json::to_string(&eval).unwrap();
947 assert!(json.contains("sess-eval"));
948 assert!(json.contains("8"));
949 assert!(json.contains("0.75"));
950 assert!(json.contains("linear"));
951 assert!(json.contains("0.82"));
952 }
953
954 #[test]
955 fn test_session_evaluation_deserialize() {
956 let json = r#"{
957 "session_id": "s-1",
958 "total_thoughts": 10,
959 "average_confidence": 0.7,
960 "mode_distribution": {"linear": 6, "divergent": 4},
961 "coherence_score": 0.9,
962 "recommendation": "Good quality"
963 }"#;
964 let eval: SessionEvaluation = serde_json::from_str(json).unwrap();
965
966 assert_eq!(eval.session_id, "s-1");
967 assert_eq!(eval.total_thoughts, 10);
968 assert_eq!(eval.average_confidence, 0.7);
969 assert_eq!(eval.mode_distribution.get("linear"), Some(&6));
970 assert_eq!(eval.coherence_score, 0.9);
971 assert_eq!(eval.recommendation, "Good quality");
972 }
973
974 #[test]
979 fn test_reflection_params_roundtrip() {
980 let original = ReflectionParams::for_thought("t-1")
981 .with_session("sess-1")
982 .with_branch("branch-1")
983 .with_max_iterations(4)
984 .with_quality_threshold(0.85)
985 .with_chain();
986
987 let json = serde_json::to_string(&original).unwrap();
988 let deserialized: ReflectionParams = serde_json::from_str(&json).unwrap();
989
990 assert_eq!(original.thought_id, deserialized.thought_id);
991 assert_eq!(original.session_id, deserialized.session_id);
992 assert_eq!(original.branch_id, deserialized.branch_id);
993 assert_eq!(original.max_iterations, deserialized.max_iterations);
994 assert_eq!(original.quality_threshold, deserialized.quality_threshold);
995 assert_eq!(original.include_chain, deserialized.include_chain);
996 }
997
998 #[test]
999 fn test_reflection_response_roundtrip() {
1000 let original = ReflectionResponse {
1001 analysis: "Deep analysis".to_string(),
1002 strengths: vec!["S1".to_string(), "S2".to_string()],
1003 weaknesses: vec!["W1".to_string()],
1004 recommendations: vec!["R1".to_string(), "R2".to_string()],
1005 confidence: 0.87,
1006 quality_score: Some(0.92),
1007 improved_thought: Some("Improved version".to_string()),
1008 metadata: serde_json::json!({"extra": "data"}),
1009 };
1010
1011 let json = serde_json::to_string(&original).unwrap();
1012 let deserialized: ReflectionResponse = serde_json::from_str(&json).unwrap();
1013
1014 assert_eq!(original.analysis, deserialized.analysis);
1015 assert_eq!(original.strengths, deserialized.strengths);
1016 assert_eq!(original.weaknesses, deserialized.weaknesses);
1017 assert_eq!(original.recommendations, deserialized.recommendations);
1018 assert_eq!(original.confidence, deserialized.confidence);
1019 assert_eq!(original.quality_score, deserialized.quality_score);
1020 assert_eq!(original.improved_thought, deserialized.improved_thought);
1021 }
1022
1023 #[test]
1024 fn test_reflection_result_roundtrip() {
1025 let original = ReflectionResult {
1026 session_id: "s-123".to_string(),
1027 reflection_thought_id: "t-refl".to_string(),
1028 original_thought_id: Some("t-orig".to_string()),
1029 analysis: "Complete analysis".to_string(),
1030 strengths: vec!["Strength".to_string()],
1031 weaknesses: vec!["Weakness".to_string()],
1032 recommendations: vec!["Recommendation".to_string()],
1033 quality_score: 0.88,
1034 improved_thought: Some(ImprovedThought {
1035 thought_id: "t-imp".to_string(),
1036 content: "Better".to_string(),
1037 confidence: 0.91,
1038 }),
1039 iterations_performed: 3,
1040 quality_improved: true,
1041 branch_id: Some("br-1".to_string()),
1042 };
1043
1044 let json = serde_json::to_string(&original).unwrap();
1045 let deserialized: ReflectionResult = serde_json::from_str(&json).unwrap();
1046
1047 assert_eq!(original.session_id, deserialized.session_id);
1048 assert_eq!(
1049 original.reflection_thought_id,
1050 deserialized.reflection_thought_id
1051 );
1052 assert_eq!(
1053 original.original_thought_id,
1054 deserialized.original_thought_id
1055 );
1056 assert_eq!(original.quality_score, deserialized.quality_score);
1057 assert_eq!(
1058 original.iterations_performed,
1059 deserialized.iterations_performed
1060 );
1061 assert_eq!(original.quality_improved, deserialized.quality_improved);
1062 assert_eq!(original.branch_id, deserialized.branch_id);
1063 }
1064
1065 #[test]
1066 fn test_session_evaluation_roundtrip() {
1067 let mut mode_dist = std::collections::HashMap::new();
1068 mode_dist.insert("linear".to_string(), 7);
1069 mode_dist.insert("tree".to_string(), 3);
1070 mode_dist.insert("reflection".to_string(), 2);
1071
1072 let original = SessionEvaluation {
1073 session_id: "eval-session".to_string(),
1074 total_thoughts: 12,
1075 average_confidence: 0.73,
1076 mode_distribution: mode_dist,
1077 coherence_score: 0.85,
1078 recommendation: "Continue with current approach".to_string(),
1079 };
1080
1081 let json = serde_json::to_string(&original).unwrap();
1082 let deserialized: SessionEvaluation = serde_json::from_str(&json).unwrap();
1083
1084 assert_eq!(original.session_id, deserialized.session_id);
1085 assert_eq!(original.total_thoughts, deserialized.total_thoughts);
1086 assert_eq!(original.average_confidence, deserialized.average_confidence);
1087 assert_eq!(original.coherence_score, deserialized.coherence_score);
1088 assert_eq!(original.recommendation, deserialized.recommendation);
1089 assert_eq!(
1090 original.mode_distribution.len(),
1091 deserialized.mode_distribution.len()
1092 );
1093 }
1094
1095 #[test]
1100 fn test_reflection_params_empty_strings() {
1101 let params = ReflectionParams::for_content("");
1102 assert_eq!(params.content, Some("".to_string()));
1103 }
1104
1105 #[test]
1106 fn test_reflection_params_builder_empty_session() {
1107 let params = ReflectionParams::for_thought("t-1").with_session("");
1108 assert_eq!(params.session_id, Some("".to_string()));
1109 }
1110
1111 #[test]
1112 fn test_reflection_params_builder_empty_branch() {
1113 let params = ReflectionParams::for_thought("t-1").with_branch("");
1114 assert_eq!(params.branch_id, Some("".to_string()));
1115 }
1116
1117 #[test]
1118 fn test_reflection_params_max_iterations_boundary() {
1119 let params1 = ReflectionParams::for_thought("t-1").with_max_iterations(1);
1121 assert_eq!(params1.max_iterations, 1);
1122
1123 let params5 = ReflectionParams::for_thought("t-1").with_max_iterations(5);
1124 assert_eq!(params5.max_iterations, 5);
1125 }
1126
1127 #[test]
1128 fn test_reflection_params_quality_threshold_boundary() {
1129 let params0 = ReflectionParams::for_thought("t-1").with_quality_threshold(0.0);
1131 assert_eq!(params0.quality_threshold, 0.0);
1132
1133 let params1 = ReflectionParams::for_thought("t-1").with_quality_threshold(1.0);
1134 assert_eq!(params1.quality_threshold, 1.0);
1135 }
1136
1137 #[test]
1138 fn test_reflection_response_empty_arrays() {
1139 let response = ReflectionResponse {
1140 analysis: "Analysis".to_string(),
1141 strengths: vec![],
1142 weaknesses: vec![],
1143 recommendations: vec![],
1144 confidence: 0.5,
1145 quality_score: None,
1146 improved_thought: None,
1147 metadata: serde_json::Value::Null,
1148 };
1149
1150 let json = serde_json::to_string(&response).unwrap();
1151 let deserialized: ReflectionResponse = serde_json::from_str(&json).unwrap();
1152
1153 assert!(deserialized.strengths.is_empty());
1154 assert!(deserialized.weaknesses.is_empty());
1155 assert!(deserialized.recommendations.is_empty());
1156 }
1157
1158 #[test]
1159 fn test_improved_thought_roundtrip() {
1160 let original = ImprovedThought {
1161 thought_id: "imp-123".to_string(),
1162 content: "Significantly improved reasoning".to_string(),
1163 confidence: 0.95,
1164 };
1165
1166 let json = serde_json::to_string(&original).unwrap();
1167 let deserialized: ImprovedThought = serde_json::from_str(&json).unwrap();
1168
1169 assert_eq!(original.thought_id, deserialized.thought_id);
1170 assert_eq!(original.content, deserialized.content);
1171 assert_eq!(original.confidence, deserialized.confidence);
1172 }
1173
1174 #[test]
1175 fn test_reflection_params_deserialize_with_defaults() {
1176 let json = r#"{"content": "Test content"}"#;
1178 let params: ReflectionParams = serde_json::from_str(json).unwrap();
1179
1180 assert_eq!(params.max_iterations, 3);
1181 assert_eq!(params.quality_threshold, 0.8);
1182 assert!(!params.include_chain);
1183 }
1184
1185 #[test]
1186 fn test_reflection_response_quality_score_none() {
1187 let json = r#"{
1188 "analysis": "Test",
1189 "strengths": [],
1190 "weaknesses": [],
1191 "recommendations": [],
1192 "confidence": 0.6
1193 }"#;
1194 let response: ReflectionResponse = serde_json::from_str(json).unwrap();
1195
1196 assert!(response.quality_score.is_none());
1197 assert!(response.improved_thought.is_none());
1198 }
1199
1200 #[test]
1201 fn test_reflection_params_very_long_content() {
1202 let long_content = "x".repeat(10000);
1203 let params = ReflectionParams::for_content(&long_content);
1204 assert_eq!(params.content.as_ref().unwrap().len(), 10000);
1205 }
1206
1207 #[test]
1208 fn test_reflection_params_special_characters() {
1209 let special = "Test with \n newlines \t tabs and \"quotes\" and 'apostrophes'";
1210 let params = ReflectionParams::for_content(special);
1211 assert_eq!(params.content, Some(special.to_string()));
1212 }
1213
1214 #[test]
1215 fn test_reflection_params_unicode() {
1216 let unicode = "Unicode test: 你好世界 🌍 مرحبا";
1217 let params = ReflectionParams::for_content(unicode);
1218 assert_eq!(params.content, Some(unicode.to_string()));
1219 }
1220
1221 #[test]
1222 fn test_reflection_result_none_values() {
1223 let result = ReflectionResult {
1224 session_id: "s-1".to_string(),
1225 reflection_thought_id: "t-r".to_string(),
1226 original_thought_id: None,
1227 analysis: "Analysis".to_string(),
1228 strengths: vec![],
1229 weaknesses: vec![],
1230 recommendations: vec![],
1231 quality_score: 0.5,
1232 improved_thought: None,
1233 iterations_performed: 1,
1234 quality_improved: false,
1235 branch_id: None,
1236 };
1237
1238 assert!(result.original_thought_id.is_none());
1239 assert!(result.improved_thought.is_none());
1240 assert!(result.branch_id.is_none());
1241 }
1242
1243 #[test]
1244 fn test_session_evaluation_empty_mode_distribution() {
1245 let eval = SessionEvaluation {
1246 session_id: "s-1".to_string(),
1247 total_thoughts: 0,
1248 average_confidence: 0.0,
1249 mode_distribution: std::collections::HashMap::new(),
1250 coherence_score: 0.0,
1251 recommendation: "No data".to_string(),
1252 };
1253
1254 assert!(eval.mode_distribution.is_empty());
1255 assert_eq!(eval.total_thoughts, 0);
1256 }
1257
1258 #[test]
1263 fn test_default_values_consistency() {
1264 let params_thought = ReflectionParams::for_thought("t-1");
1266 let params_content = ReflectionParams::for_content("content");
1267
1268 assert_eq!(params_thought.max_iterations, default_max_iterations());
1269 assert_eq!(
1270 params_thought.quality_threshold,
1271 default_quality_threshold()
1272 );
1273 assert_eq!(params_content.max_iterations, default_max_iterations());
1274 assert_eq!(
1275 params_content.quality_threshold,
1276 default_quality_threshold()
1277 );
1278 }
1279
1280 #[test]
1281 fn test_clamp_values_negative() {
1282 let params = ReflectionParams::for_thought("t-1")
1284 .with_max_iterations(0)
1285 .with_quality_threshold(-1.0);
1286
1287 assert_eq!(params.max_iterations, 1);
1288 assert_eq!(params.quality_threshold, 0.0);
1289 }
1290
1291 #[test]
1292 fn test_clamp_values_very_high() {
1293 let params = ReflectionParams::for_thought("t-1")
1295 .with_max_iterations(100)
1296 .with_quality_threshold(5.0);
1297
1298 assert_eq!(params.max_iterations, 5);
1299 assert_eq!(params.quality_threshold, 1.0);
1300 }
1301
1302 #[test]
1303 fn test_reflection_result_quality_improved_logic() {
1304 let result_improved = ReflectionResult {
1306 session_id: "s-1".to_string(),
1307 reflection_thought_id: "t-1".to_string(),
1308 original_thought_id: Some("t-orig".to_string()),
1309 analysis: "A".to_string(),
1310 strengths: vec![],
1311 weaknesses: vec![],
1312 recommendations: vec![],
1313 quality_score: 0.9,
1314 improved_thought: None,
1315 iterations_performed: 1,
1316 quality_improved: true,
1317 branch_id: None,
1318 };
1319
1320 assert!(result_improved.quality_improved);
1321 assert!(result_improved.quality_score > 0.5);
1322 }
1323
1324 #[test]
1329 fn test_reflection_params_multiple_with_calls() {
1330 let params = ReflectionParams::for_thought("t-1")
1331 .with_session("s1")
1332 .with_session("s2") .with_max_iterations(2)
1334 .with_max_iterations(4); assert_eq!(params.session_id, Some("s2".to_string()));
1337 assert_eq!(params.max_iterations, 4);
1338 }
1339
1340 #[test]
1341 fn test_reflection_params_content_builder_full_chain() {
1342 let params = ReflectionParams::for_content("My content")
1343 .with_session("session-abc")
1344 .with_branch("branch-xyz")
1345 .with_max_iterations(2)
1346 .with_quality_threshold(0.75)
1347 .with_chain();
1348
1349 assert!(params.thought_id.is_none());
1350 assert_eq!(params.content, Some("My content".to_string()));
1351 assert_eq!(params.session_id, Some("session-abc".to_string()));
1352 assert_eq!(params.branch_id, Some("branch-xyz".to_string()));
1353 assert_eq!(params.max_iterations, 2);
1354 assert_eq!(params.quality_threshold, 0.75);
1355 assert!(params.include_chain);
1356 }
1357
1358 #[test]
1359 fn test_reflection_params_default_include_chain_false() {
1360 let params1 = ReflectionParams::for_thought("t-1");
1361 let params2 = ReflectionParams::for_content("content");
1362
1363 assert!(!params1.include_chain);
1364 assert!(!params2.include_chain);
1365 }
1366
1367 #[test]
1368 fn test_reflection_response_metadata_default() {
1369 let json = r#"{
1370 "analysis": "Test",
1371 "strengths": [],
1372 "weaknesses": [],
1373 "recommendations": [],
1374 "confidence": 0.5
1375 }"#;
1376 let response: ReflectionResponse = serde_json::from_str(json).unwrap();
1377
1378 assert_eq!(response.metadata, serde_json::Value::Null);
1380 }
1381
1382 #[test]
1383 fn test_reflection_params_skip_serializing_none() {
1384 let params = ReflectionParams::for_content("content");
1385 let json = serde_json::to_string(¶ms).unwrap();
1386
1387 assert!(!json.contains("thought_id"));
1389 assert!(!json.contains("session_id"));
1390 assert!(!json.contains("branch_id"));
1391 }
1392
1393 #[test]
1394 fn test_reflection_result_skip_serializing_branch_none() {
1395 let result = ReflectionResult {
1396 session_id: "s-1".to_string(),
1397 reflection_thought_id: "t-1".to_string(),
1398 original_thought_id: None,
1399 analysis: "A".to_string(),
1400 strengths: vec![],
1401 weaknesses: vec![],
1402 recommendations: vec![],
1403 quality_score: 0.5,
1404 improved_thought: None,
1405 iterations_performed: 1,
1406 quality_improved: false,
1407 branch_id: None,
1408 };
1409
1410 let json = serde_json::to_string(&result).unwrap();
1411 assert!(!json.contains("branch_id"));
1412 }
1413
1414 #[test]
1415 fn test_reflection_result_with_all_fields() {
1416 let result = ReflectionResult {
1417 session_id: "s-1".to_string(),
1418 reflection_thought_id: "t-refl".to_string(),
1419 original_thought_id: Some("t-orig".to_string()),
1420 analysis: "Full analysis".to_string(),
1421 strengths: vec!["S1".to_string(), "S2".to_string()],
1422 weaknesses: vec!["W1".to_string()],
1423 recommendations: vec!["R1".to_string(), "R2".to_string(), "R3".to_string()],
1424 quality_score: 0.88,
1425 improved_thought: Some(ImprovedThought {
1426 thought_id: "t-imp".to_string(),
1427 content: "Improved".to_string(),
1428 confidence: 0.92,
1429 }),
1430 iterations_performed: 4,
1431 quality_improved: true,
1432 branch_id: Some("b-1".to_string()),
1433 };
1434
1435 assert_eq!(result.strengths.len(), 2);
1436 assert_eq!(result.weaknesses.len(), 1);
1437 assert_eq!(result.recommendations.len(), 3);
1438 assert!(result.improved_thought.is_some());
1439 assert!(result.branch_id.is_some());
1440 }
1441
1442 #[test]
1443 fn test_session_evaluation_single_mode() {
1444 let mut mode_dist = std::collections::HashMap::new();
1445 mode_dist.insert("reflection".to_string(), 1);
1446
1447 let eval = SessionEvaluation {
1448 session_id: "s-1".to_string(),
1449 total_thoughts: 1,
1450 average_confidence: 0.9,
1451 mode_distribution: mode_dist,
1452 coherence_score: 1.0,
1453 recommendation: "Single thought".to_string(),
1454 };
1455
1456 assert_eq!(eval.mode_distribution.len(), 1);
1457 assert_eq!(eval.total_thoughts, 1);
1458 }
1459
1460 #[test]
1461 fn test_improved_thought_zero_confidence() {
1462 let improved = ImprovedThought {
1463 thought_id: "t-1".to_string(),
1464 content: "Low confidence improvement".to_string(),
1465 confidence: 0.0,
1466 };
1467
1468 assert_eq!(improved.confidence, 0.0);
1469 }
1470
1471 #[test]
1472 fn test_improved_thought_full_confidence() {
1473 let improved = ImprovedThought {
1474 thought_id: "t-1".to_string(),
1475 content: "Perfect improvement".to_string(),
1476 confidence: 1.0,
1477 };
1478
1479 assert_eq!(improved.confidence, 1.0);
1480 }
1481
1482 #[test]
1483 fn test_reflection_response_with_complex_metadata() {
1484 let response = ReflectionResponse {
1485 analysis: "Complex".to_string(),
1486 strengths: vec![],
1487 weaknesses: vec![],
1488 recommendations: vec![],
1489 confidence: 0.5,
1490 quality_score: None,
1491 improved_thought: None,
1492 metadata: serde_json::json!({
1493 "nested": {
1494 "field": "value",
1495 "array": [1, 2, 3]
1496 },
1497 "boolean": true,
1498 "number": 42
1499 }),
1500 };
1501
1502 let json = serde_json::to_string(&response).unwrap();
1503 let deserialized: ReflectionResponse = serde_json::from_str(&json).unwrap();
1504
1505 assert_eq!(response.metadata["nested"]["field"], "value");
1506 assert_eq!(deserialized.metadata["boolean"], true);
1507 }
1508
1509 fn create_test_config() -> Config {
1514 use crate::config::{
1515 DatabaseConfig, ErrorHandlingConfig, LangbaseConfig, LogFormat, LoggingConfig,
1516 PipeConfig,
1517 };
1518 use std::path::PathBuf;
1519
1520 Config {
1521 langbase: LangbaseConfig {
1522 api_key: "test-key".to_string(),
1523 base_url: "https://api.langbase.com".to_string(),
1524 },
1525 database: DatabaseConfig {
1526 path: PathBuf::from(":memory:"),
1527 max_connections: 5,
1528 },
1529 logging: LoggingConfig {
1530 level: "info".to_string(),
1531 format: LogFormat::Pretty,
1532 },
1533 request: crate::config::RequestConfig::default(),
1534 pipes: PipeConfig::default(),
1535 error_handling: ErrorHandlingConfig::default(),
1536 }
1537 }
1538
1539 #[test]
1540 fn test_reflection_mode_new() {
1541 let config = create_test_config();
1542 let rt = tokio::runtime::Runtime::new().unwrap();
1543 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1544 let langbase =
1545 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1546
1547 let mode = ReflectionMode::new(storage, langbase, &config);
1548 assert_eq!(mode.pipe_name, config.pipes.reflection);
1549 }
1550
1551 #[test]
1552 fn test_reflection_mode_new_with_custom_pipe_name() {
1553 let mut config = create_test_config();
1554 config.pipes.reflection = "custom-reflection-pipe".to_string();
1555
1556 let rt = tokio::runtime::Runtime::new().unwrap();
1557 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1558 let langbase =
1559 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1560
1561 let mode = ReflectionMode::new(storage, langbase, &config);
1562 assert_eq!(mode.pipe_name, "custom-reflection-pipe");
1563 }
1564
1565 #[test]
1570 fn test_build_messages_simple_content() {
1571 use crate::langbase::MessageRole;
1572
1573 let config = create_test_config();
1574 let rt = tokio::runtime::Runtime::new().unwrap();
1575 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1576 let langbase =
1577 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1578
1579 let mode = ReflectionMode::new(storage, langbase, &config);
1580 let messages = mode.build_messages("Test content", &[], 0);
1581
1582 assert_eq!(messages.len(), 2);
1583 assert!(matches!(messages[0].role, MessageRole::System));
1584 assert!(messages[0].content.contains("meta-cognitive"));
1585 assert!(matches!(messages[1].role, MessageRole::User));
1586 assert!(messages[1].content.contains("Test content"));
1587 }
1588
1589 #[test]
1590 fn test_build_messages_with_iteration() {
1591 let config = create_test_config();
1592 let rt = tokio::runtime::Runtime::new().unwrap();
1593 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1594 let langbase =
1595 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1596
1597 let mode = ReflectionMode::new(storage, langbase, &config);
1598 let messages = mode.build_messages("Test content", &[], 2);
1599
1600 assert_eq!(messages.len(), 2);
1601 assert!(messages[0].content.contains("iteration 3"));
1602 assert!(messages[0]
1603 .content
1604 .contains("previously identified weaknesses"));
1605 }
1606
1607 #[test]
1608 fn test_build_messages_with_empty_chain() {
1609 let config = create_test_config();
1610 let rt = tokio::runtime::Runtime::new().unwrap();
1611 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1612 let langbase =
1613 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1614
1615 let mode = ReflectionMode::new(storage, langbase, &config);
1616 let chain: Vec<Thought> = vec![];
1617 let messages = mode.build_messages("Content", &chain, 0);
1618
1619 assert_eq!(messages.len(), 2);
1620 assert!(!messages
1621 .iter()
1622 .any(|m| m.content.contains("Reasoning chain")));
1623 }
1624
1625 #[test]
1626 fn test_build_messages_with_reasoning_chain() {
1627 use crate::langbase::MessageRole;
1628
1629 let config = create_test_config();
1630 let rt = tokio::runtime::Runtime::new().unwrap();
1631 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1632 let langbase =
1633 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1634
1635 let mode = ReflectionMode::new(storage, langbase, &config);
1636
1637 let thought1 = Thought::new("sess-1", "First thought", "linear").with_confidence(0.7);
1638 let thought2 = Thought::new("sess-1", "Second thought", "tree").with_confidence(0.8);
1639 let chain = vec![thought1, thought2];
1640
1641 let messages = mode.build_messages("Final thought", &chain, 0);
1642
1643 assert_eq!(messages.len(), 3);
1644 assert!(matches!(messages[0].role, MessageRole::System));
1645 assert!(matches!(messages[1].role, MessageRole::User));
1646 assert!(messages[1].content.contains("Reasoning chain"));
1647 assert!(messages[1].content.contains("First thought"));
1648 assert!(messages[1].content.contains("Second thought"));
1649 assert!(messages[1].content.contains("0.70"));
1650 assert!(messages[1].content.contains("0.80"));
1651 assert!(matches!(messages[2].role, MessageRole::User));
1652 assert!(messages[2].content.contains("Final thought"));
1653 }
1654
1655 #[test]
1656 fn test_build_messages_chain_formatting() {
1657 let config = create_test_config();
1658 let rt = tokio::runtime::Runtime::new().unwrap();
1659 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1660 let langbase =
1661 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1662
1663 let mode = ReflectionMode::new(storage, langbase, &config);
1664
1665 let thought = Thought::new("sess-1", "Content", "divergent").with_confidence(0.65);
1666 let chain = vec![thought];
1667
1668 let messages = mode.build_messages("Test", &chain, 0);
1669
1670 let chain_message = &messages[1];
1671 assert!(chain_message.content.contains("[divergent]"));
1672 assert!(chain_message.content.contains("confidence: 0.65"));
1673 assert!(chain_message.content.contains("Content"));
1674 }
1675
1676 #[test]
1677 fn test_build_messages_empty_content() {
1678 let config = create_test_config();
1679 let rt = tokio::runtime::Runtime::new().unwrap();
1680 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1681 let langbase =
1682 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1683
1684 let mode = ReflectionMode::new(storage, langbase, &config);
1685 let messages = mode.build_messages("", &[], 0);
1686
1687 assert_eq!(messages.len(), 2);
1688 assert!(messages[1].content.contains("Thought to reflect upon"));
1689 }
1690
1691 #[test]
1692 fn test_build_messages_unicode_content() {
1693 let config = create_test_config();
1694 let rt = tokio::runtime::Runtime::new().unwrap();
1695 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1696 let langbase =
1697 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1698
1699 let mode = ReflectionMode::new(storage, langbase, &config);
1700 let unicode_content = "Test with unicode: 你好 🌍 مرحبا";
1701 let messages = mode.build_messages(unicode_content, &[], 0);
1702
1703 assert!(messages[1].content.contains(unicode_content));
1704 }
1705
1706 #[test]
1707 fn test_build_messages_special_characters() {
1708 let config = create_test_config();
1709 let rt = tokio::runtime::Runtime::new().unwrap();
1710 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1711 let langbase =
1712 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1713
1714 let mode = ReflectionMode::new(storage, langbase, &config);
1715 let special = "Content with\nnewlines\tand\ttabs and \"quotes\"";
1716 let messages = mode.build_messages(special, &[], 0);
1717
1718 assert!(messages[1].content.contains(special));
1719 }
1720
1721 #[test]
1722 fn test_build_messages_large_chain() {
1723 let config = create_test_config();
1724 let rt = tokio::runtime::Runtime::new().unwrap();
1725 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1726 let langbase =
1727 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1728
1729 let mode = ReflectionMode::new(storage, langbase, &config);
1730
1731 let mut chain = Vec::new();
1732 for i in 0..5 {
1733 chain.push(Thought::new("sess-1", format!("Thought {}", i), "linear"));
1734 }
1735
1736 let messages = mode.build_messages("Final", &chain, 0);
1737
1738 assert_eq!(messages.len(), 3);
1739 let chain_msg = &messages[1];
1740 assert!(chain_msg.content.contains("Thought 0"));
1741 assert!(chain_msg.content.contains("Thought 4"));
1742 }
1743
1744 #[test]
1749 fn test_parse_response_valid_json() {
1750 let config = create_test_config();
1751 let rt = tokio::runtime::Runtime::new().unwrap();
1752 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1753 let langbase =
1754 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1755
1756 let mode = ReflectionMode::new(storage, langbase, &config);
1757
1758 let completion = r#"{
1759 "analysis": "This is the analysis",
1760 "strengths": ["Strength 1", "Strength 2"],
1761 "weaknesses": ["Weakness 1"],
1762 "recommendations": ["Recommendation 1"],
1763 "confidence": 0.85
1764 }"#;
1765
1766 let response = mode.parse_response(completion).unwrap();
1767 assert_eq!(response.analysis, "This is the analysis");
1768 assert_eq!(response.strengths.len(), 2);
1769 assert_eq!(response.weaknesses.len(), 1);
1770 assert_eq!(response.recommendations.len(), 1);
1771 assert_eq!(response.confidence, 0.85);
1772 }
1773
1774 #[test]
1775 fn test_parse_response_with_quality_score() {
1776 let config = create_test_config();
1777 let rt = tokio::runtime::Runtime::new().unwrap();
1778 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1779 let langbase =
1780 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1781
1782 let mode = ReflectionMode::new(storage, langbase, &config);
1783
1784 let completion = r#"{
1785 "analysis": "Analysis",
1786 "strengths": [],
1787 "weaknesses": [],
1788 "recommendations": [],
1789 "confidence": 0.7,
1790 "quality_score": 0.9
1791 }"#;
1792
1793 let response = mode.parse_response(completion).unwrap();
1794 assert_eq!(response.quality_score, Some(0.9));
1795 }
1796
1797 #[test]
1798 fn test_parse_response_with_improved_thought() {
1799 let config = create_test_config();
1800 let rt = tokio::runtime::Runtime::new().unwrap();
1801 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1802 let langbase =
1803 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1804
1805 let mode = ReflectionMode::new(storage, langbase, &config);
1806
1807 let completion = r#"{
1808 "analysis": "Analysis",
1809 "strengths": [],
1810 "weaknesses": [],
1811 "recommendations": [],
1812 "confidence": 0.8,
1813 "improved_thought": "This is the improved version"
1814 }"#;
1815
1816 let response = mode.parse_response(completion).unwrap();
1817 assert_eq!(
1818 response.improved_thought,
1819 Some("This is the improved version".to_string())
1820 );
1821 }
1822
1823 #[test]
1824 fn test_parse_response_with_metadata() {
1825 let config = create_test_config();
1826 let rt = tokio::runtime::Runtime::new().unwrap();
1827 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1828 let langbase =
1829 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1830
1831 let mode = ReflectionMode::new(storage, langbase, &config);
1832
1833 let completion = r#"{
1834 "analysis": "Analysis",
1835 "strengths": [],
1836 "weaknesses": [],
1837 "recommendations": [],
1838 "confidence": 0.6,
1839 "metadata": {"key": "value", "number": 42}
1840 }"#;
1841
1842 let response = mode.parse_response(completion).unwrap();
1843 assert_eq!(response.metadata["key"], "value");
1844 assert_eq!(response.metadata["number"], 42);
1845 }
1846
1847 #[test]
1848 fn test_parse_response_json_with_markdown() {
1849 let config = create_test_config();
1850 let rt = tokio::runtime::Runtime::new().unwrap();
1851 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1852 let langbase =
1853 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1854
1855 let mode = ReflectionMode::new(storage, langbase, &config);
1856
1857 let completion = r#"Here's my analysis:
1858
1859```json
1860{
1861 "analysis": "Extracted from markdown",
1862 "strengths": ["S1"],
1863 "weaknesses": ["W1"],
1864 "recommendations": ["R1"],
1865 "confidence": 0.75
1866}
1867```"#;
1868
1869 let response = mode.parse_response(completion).unwrap();
1870 assert_eq!(response.analysis, "Extracted from markdown");
1871 assert_eq!(response.confidence, 0.75);
1872 }
1873
1874 #[test]
1875 fn test_parse_response_invalid_json() {
1876 let config = create_test_config();
1877 let rt = tokio::runtime::Runtime::new().unwrap();
1878 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1879 let langbase =
1880 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1881
1882 let mode = ReflectionMode::new(storage, langbase, &config);
1883
1884 let completion = "This is not JSON at all";
1885 let result = mode.parse_response(completion);
1886 assert!(result.is_err());
1887 }
1888
1889 #[test]
1890 fn test_parse_response_missing_required_fields() {
1891 let config = create_test_config();
1892 let rt = tokio::runtime::Runtime::new().unwrap();
1893 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1894 let langbase =
1895 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1896
1897 let mode = ReflectionMode::new(storage, langbase, &config);
1898
1899 let completion = r#"{"analysis": "Missing other fields"}"#;
1900 let result = mode.parse_response(completion);
1901 assert!(result.is_err());
1902 }
1903
1904 #[test]
1905 fn test_parse_response_empty_arrays() {
1906 let config = create_test_config();
1907 let rt = tokio::runtime::Runtime::new().unwrap();
1908 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1909 let langbase =
1910 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1911
1912 let mode = ReflectionMode::new(storage, langbase, &config);
1913
1914 let completion = r#"{
1915 "analysis": "Test",
1916 "strengths": [],
1917 "weaknesses": [],
1918 "recommendations": [],
1919 "confidence": 0.5
1920 }"#;
1921
1922 let response = mode.parse_response(completion).unwrap();
1923 assert!(response.strengths.is_empty());
1924 assert!(response.weaknesses.is_empty());
1925 assert!(response.recommendations.is_empty());
1926 }
1927
1928 #[test]
1929 fn test_parse_response_unicode_in_analysis() {
1930 let config = create_test_config();
1931 let rt = tokio::runtime::Runtime::new().unwrap();
1932 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1933 let langbase =
1934 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1935
1936 let mode = ReflectionMode::new(storage, langbase, &config);
1937
1938 let completion = r#"{
1939 "analysis": "Unicode: 你好世界 🌍",
1940 "strengths": [],
1941 "weaknesses": [],
1942 "recommendations": [],
1943 "confidence": 0.5
1944 }"#;
1945
1946 let response = mode.parse_response(completion).unwrap();
1947 assert!(response.analysis.contains("你好世界"));
1948 assert!(response.analysis.contains("🌍"));
1949 }
1950
1951 #[test]
1956 fn test_calculate_coherence_empty_thoughts() {
1957 let config = create_test_config();
1958 let rt = tokio::runtime::Runtime::new().unwrap();
1959 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1960 let langbase =
1961 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1962
1963 let mode = ReflectionMode::new(storage, langbase, &config);
1964 let thoughts: Vec<Thought> = vec![];
1965 let coherence = mode.calculate_coherence(&thoughts);
1966
1967 assert_eq!(coherence, 1.0);
1969 }
1970
1971 #[test]
1972 fn test_calculate_coherence_single_thought() {
1973 let config = create_test_config();
1974 let rt = tokio::runtime::Runtime::new().unwrap();
1975 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1976 let langbase =
1977 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1978
1979 let mode = ReflectionMode::new(storage, langbase, &config);
1980 let thought = Thought::new("sess-1", "Single thought", "linear");
1981 let coherence = mode.calculate_coherence(&[thought]);
1982
1983 assert_eq!(coherence, 1.0);
1984 }
1985
1986 #[test]
1987 fn test_calculate_coherence_all_linked() {
1988 let config = create_test_config();
1989 let rt = tokio::runtime::Runtime::new().unwrap();
1990 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
1991 let langbase =
1992 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
1993
1994 let mode = ReflectionMode::new(storage, langbase, &config);
1995
1996 let thought1 = Thought::new("sess-1", "First", "linear").with_confidence(0.8);
1997 let thought2 = Thought::new("sess-1", "Second", "linear")
1998 .with_confidence(0.8)
1999 .with_parent(&thought1.id);
2000 let thought3 = Thought::new("sess-1", "Third", "linear")
2001 .with_confidence(0.8)
2002 .with_parent(&thought2.id);
2003
2004 let coherence = mode.calculate_coherence(&[thought1, thought2, thought3]);
2005
2006 assert!(coherence > 0.9);
2008 }
2009
2010 #[test]
2011 fn test_calculate_coherence_no_links() {
2012 let config = create_test_config();
2013 let rt = tokio::runtime::Runtime::new().unwrap();
2014 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2015 let langbase =
2016 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2017
2018 let mode = ReflectionMode::new(storage, langbase, &config);
2019
2020 let thought1 = Thought::new("sess-1", "First", "linear").with_confidence(0.8);
2021 let thought2 = Thought::new("sess-1", "Second", "linear").with_confidence(0.8);
2022 let thought3 = Thought::new("sess-1", "Third", "linear").with_confidence(0.8);
2023
2024 let coherence = mode.calculate_coherence(&[thought1, thought2, thought3]);
2025
2026 assert!(coherence < 0.7);
2028 }
2029
2030 #[test]
2031 fn test_calculate_coherence_unstable_confidence() {
2032 let config = create_test_config();
2033 let rt = tokio::runtime::Runtime::new().unwrap();
2034 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2035 let langbase =
2036 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2037
2038 let mode = ReflectionMode::new(storage, langbase, &config);
2039
2040 let thought1 = Thought::new("sess-1", "First", "linear").with_confidence(0.9);
2041 let thought2 = Thought::new("sess-1", "Second", "linear").with_confidence(0.1);
2042 let thought3 = Thought::new("sess-1", "Third", "linear").with_confidence(0.9);
2043
2044 let coherence = mode.calculate_coherence(&[thought1, thought2, thought3]);
2045
2046 assert!(coherence < 0.7);
2048 }
2049
2050 #[test]
2051 fn test_calculate_coherence_partial_links() {
2052 let config = create_test_config();
2053 let rt = tokio::runtime::Runtime::new().unwrap();
2054 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2055 let langbase =
2056 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2057
2058 let mode = ReflectionMode::new(storage, langbase, &config);
2059
2060 let thought1 = Thought::new("sess-1", "First", "linear").with_confidence(0.7);
2061 let thought2 = Thought::new("sess-1", "Second", "linear")
2062 .with_confidence(0.75)
2063 .with_parent(&thought1.id);
2064 let thought3 = Thought::new("sess-1", "Third", "linear").with_confidence(0.7);
2065
2066 let coherence = mode.calculate_coherence(&[thought1, thought2, thought3]);
2067
2068 assert!(coherence > 0.5 && coherence < 0.9);
2070 }
2071
2072 #[test]
2073 fn test_calculate_coherence_two_thoughts_linked() {
2074 let config = create_test_config();
2075 let rt = tokio::runtime::Runtime::new().unwrap();
2076 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2077 let langbase =
2078 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2079
2080 let mode = ReflectionMode::new(storage, langbase, &config);
2081
2082 let thought1 = Thought::new("sess-1", "First", "linear").with_confidence(0.8);
2083 let thought2 = Thought::new("sess-1", "Second", "linear")
2084 .with_confidence(0.8)
2085 .with_parent(&thought1.id);
2086
2087 let coherence = mode.calculate_coherence(&[thought1, thought2]);
2088
2089 assert!(coherence > 0.9);
2091 }
2092
2093 #[test]
2094 fn test_calculate_coherence_two_thoughts_unlinked() {
2095 let config = create_test_config();
2096 let rt = tokio::runtime::Runtime::new().unwrap();
2097 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2098 let langbase =
2099 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2100
2101 let mode = ReflectionMode::new(storage, langbase, &config);
2102
2103 let thought1 = Thought::new("sess-1", "First", "linear").with_confidence(0.8);
2104 let thought2 = Thought::new("sess-1", "Second", "linear").with_confidence(0.8);
2105
2106 let coherence = mode.calculate_coherence(&[thought1, thought2]);
2107
2108 assert!(coherence < 0.7);
2110 }
2111
2112 #[test]
2113 fn test_calculate_coherence_varying_confidence() {
2114 let config = create_test_config();
2115 let rt = tokio::runtime::Runtime::new().unwrap();
2116 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2117 let langbase =
2118 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2119
2120 let mode = ReflectionMode::new(storage, langbase, &config);
2121
2122 let thought1 = Thought::new("sess-1", "First", "linear").with_confidence(0.5);
2123 let thought2 = Thought::new("sess-1", "Second", "linear")
2124 .with_confidence(0.6)
2125 .with_parent(&thought1.id);
2126 let thought3 = Thought::new("sess-1", "Third", "linear")
2127 .with_confidence(0.7)
2128 .with_parent(&thought2.id);
2129
2130 let coherence = mode.calculate_coherence(&[thought1, thought2, thought3]);
2131
2132 assert!(coherence > 0.8);
2134 }
2135
2136 #[test]
2141 fn test_reflection_params_for_thought_with_empty_id() {
2142 let params = ReflectionParams::for_thought("");
2143 assert_eq!(params.thought_id, Some("".to_string()));
2144 }
2145
2146 #[test]
2147 fn test_reflection_params_for_content_with_whitespace() {
2148 let params = ReflectionParams::for_content(" \n\t ");
2149 assert_eq!(params.content, Some(" \n\t ".to_string()));
2150 }
2151
2152 #[test]
2153 fn test_reflection_response_zero_confidence() {
2154 let response = ReflectionResponse {
2155 analysis: "Low confidence".to_string(),
2156 strengths: vec![],
2157 weaknesses: vec!["Many issues".to_string()],
2158 recommendations: vec![],
2159 confidence: 0.0,
2160 quality_score: Some(0.0),
2161 improved_thought: None,
2162 metadata: serde_json::Value::Null,
2163 };
2164
2165 assert_eq!(response.confidence, 0.0);
2166 assert_eq!(response.quality_score, Some(0.0));
2167 }
2168
2169 #[test]
2170 fn test_reflection_response_perfect_confidence() {
2171 let response = ReflectionResponse {
2172 analysis: "Perfect".to_string(),
2173 strengths: vec!["Everything".to_string()],
2174 weaknesses: vec![],
2175 recommendations: vec![],
2176 confidence: 1.0,
2177 quality_score: Some(1.0),
2178 improved_thought: None,
2179 metadata: serde_json::Value::Null,
2180 };
2181
2182 assert_eq!(response.confidence, 1.0);
2183 assert_eq!(response.quality_score, Some(1.0));
2184 }
2185
2186 #[test]
2187 fn test_reflection_result_iterations_zero() {
2188 let result = ReflectionResult {
2189 session_id: "s-1".to_string(),
2190 reflection_thought_id: "t-1".to_string(),
2191 original_thought_id: None,
2192 analysis: "Quick".to_string(),
2193 strengths: vec![],
2194 weaknesses: vec![],
2195 recommendations: vec![],
2196 quality_score: 0.95,
2197 improved_thought: None,
2198 iterations_performed: 0,
2199 quality_improved: true,
2200 branch_id: None,
2201 };
2202
2203 assert_eq!(result.iterations_performed, 0);
2204 }
2205
2206 #[test]
2207 fn test_session_evaluation_high_confidence_low_coherence() {
2208 let mut mode_dist = std::collections::HashMap::new();
2209 mode_dist.insert("linear".to_string(), 5);
2210
2211 let eval = SessionEvaluation {
2212 session_id: "s-1".to_string(),
2213 total_thoughts: 5,
2214 average_confidence: 0.9,
2215 mode_distribution: mode_dist,
2216 coherence_score: 0.3,
2217 recommendation: "Reasoning chain may have logical gaps - consider reflection mode"
2218 .to_string(),
2219 };
2220
2221 assert!(eval.average_confidence > 0.6);
2222 assert!(eval.coherence_score < 0.5);
2223 assert!(eval.recommendation.contains("logical gaps"));
2224 }
2225
2226 #[test]
2227 fn test_session_evaluation_low_confidence_high_coherence() {
2228 let mut mode_dist = std::collections::HashMap::new();
2229 mode_dist.insert("linear".to_string(), 3);
2230
2231 let eval = SessionEvaluation {
2232 session_id: "s-1".to_string(),
2233 total_thoughts: 3,
2234 average_confidence: 0.4,
2235 mode_distribution: mode_dist,
2236 coherence_score: 0.9,
2237 recommendation: "Consider reviewing and refining low-confidence thoughts".to_string(),
2238 };
2239
2240 assert!(eval.average_confidence < 0.6);
2241 assert!(eval.coherence_score > 0.5);
2242 assert!(eval.recommendation.contains("low-confidence"));
2243 }
2244
2245 #[test]
2246 fn test_reflection_params_with_all_options_none() {
2247 let params = ReflectionParams {
2248 thought_id: None,
2249 content: None,
2250 session_id: None,
2251 branch_id: None,
2252 max_iterations: 1,
2253 quality_threshold: 0.5,
2254 include_chain: false,
2255 };
2256
2257 assert!(params.thought_id.is_none());
2258 assert!(params.content.is_none());
2259 assert!(params.session_id.is_none());
2260 assert!(params.branch_id.is_none());
2261 }
2262
2263 #[test]
2264 fn test_improved_thought_empty_content() {
2265 let improved = ImprovedThought {
2266 thought_id: "t-1".to_string(),
2267 content: "".to_string(),
2268 confidence: 0.5,
2269 };
2270
2271 assert_eq!(improved.content, "");
2272 }
2273
2274 #[test]
2275 fn test_reflection_response_large_arrays() {
2276 let strengths: Vec<String> = (0..100).map(|i| format!("Strength {}", i)).collect();
2277 let weaknesses: Vec<String> = (0..50).map(|i| format!("Weakness {}", i)).collect();
2278 let recommendations: Vec<String> = (0..75).map(|i| format!("Rec {}", i)).collect();
2279
2280 let response = ReflectionResponse {
2281 analysis: "Large arrays".to_string(),
2282 strengths: strengths.clone(),
2283 weaknesses: weaknesses.clone(),
2284 recommendations: recommendations.clone(),
2285 confidence: 0.5,
2286 quality_score: None,
2287 improved_thought: None,
2288 metadata: serde_json::Value::Null,
2289 };
2290
2291 assert_eq!(response.strengths.len(), 100);
2292 assert_eq!(response.weaknesses.len(), 50);
2293 assert_eq!(response.recommendations.len(), 75);
2294 }
2295
2296 #[test]
2297 fn test_reflection_params_extreme_quality_threshold() {
2298 let params1 = ReflectionParams::for_thought("t-1").with_quality_threshold(-100.0);
2299 assert_eq!(params1.quality_threshold, 0.0);
2300
2301 let params2 = ReflectionParams::for_thought("t-1").with_quality_threshold(100.0);
2302 assert_eq!(params2.quality_threshold, 1.0);
2303 }
2304
2305 #[test]
2306 fn test_reflection_params_extreme_max_iterations() {
2307 let params1 = ReflectionParams::for_thought("t-1").with_max_iterations(usize::MAX);
2308 assert_eq!(params1.max_iterations, 5);
2309
2310 let params2 = ReflectionParams::for_thought("t-1").with_max_iterations(0);
2311 assert_eq!(params2.max_iterations, 1);
2312 }
2313
2314 #[test]
2315 fn test_session_evaluation_many_modes() {
2316 let mut mode_dist = std::collections::HashMap::new();
2317 mode_dist.insert("linear".to_string(), 10);
2318 mode_dist.insert("tree".to_string(), 8);
2319 mode_dist.insert("divergent".to_string(), 5);
2320 mode_dist.insert("reflection".to_string(), 3);
2321 mode_dist.insert("backtracking".to_string(), 2);
2322
2323 let eval = SessionEvaluation {
2324 session_id: "s-complex".to_string(),
2325 total_thoughts: 28,
2326 average_confidence: 0.72,
2327 mode_distribution: mode_dist.clone(),
2328 coherence_score: 0.68,
2329 recommendation: "Reasoning quality is acceptable".to_string(),
2330 };
2331
2332 assert_eq!(eval.mode_distribution.len(), 5);
2333 assert_eq!(eval.total_thoughts, 28);
2334 assert_eq!(eval.mode_distribution.get("linear"), Some(&10));
2335 }
2336
2337 #[test]
2338 fn test_reflection_result_with_very_long_analysis() {
2339 let long_analysis = "a".repeat(10000);
2340 let result = ReflectionResult {
2341 session_id: "s-1".to_string(),
2342 reflection_thought_id: "t-1".to_string(),
2343 original_thought_id: None,
2344 analysis: long_analysis.clone(),
2345 strengths: vec![],
2346 weaknesses: vec![],
2347 recommendations: vec![],
2348 quality_score: 0.5,
2349 improved_thought: None,
2350 iterations_performed: 1,
2351 quality_improved: false,
2352 branch_id: None,
2353 };
2354
2355 assert_eq!(result.analysis.len(), 10000);
2356 }
2357
2358 #[test]
2359 fn test_parse_response_with_nested_json_arrays() {
2360 let config = create_test_config();
2361 let rt = tokio::runtime::Runtime::new().unwrap();
2362 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2363 let langbase =
2364 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2365
2366 let mode = ReflectionMode::new(storage, langbase, &config);
2367
2368 let completion = r#"{
2369 "analysis": "Complex",
2370 "strengths": ["S1", "S2", "S3"],
2371 "weaknesses": ["W1", "W2"],
2372 "recommendations": ["R1", "R2", "R3", "R4"],
2373 "confidence": 0.65,
2374 "metadata": {
2375 "nested": {
2376 "deep": {
2377 "array": [1, 2, 3]
2378 }
2379 }
2380 }
2381 }"#;
2382
2383 let response = mode.parse_response(completion).unwrap();
2384 assert_eq!(response.strengths.len(), 3);
2385 assert_eq!(response.weaknesses.len(), 2);
2386 assert_eq!(response.recommendations.len(), 4);
2387 assert_eq!(response.metadata["nested"]["deep"]["array"][0], 1);
2388 }
2389
2390 #[test]
2391 fn test_reflection_params_quality_threshold_precision() {
2392 let params = ReflectionParams::for_thought("t-1").with_quality_threshold(0.123456789);
2393 assert!((params.quality_threshold - 0.123456789).abs() < 1e-9);
2394 }
2395
2396 #[test]
2397 fn test_build_messages_iteration_zero() {
2398 let config = create_test_config();
2399 let rt = tokio::runtime::Runtime::new().unwrap();
2400 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2401 let langbase =
2402 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2403
2404 let mode = ReflectionMode::new(storage, langbase, &config);
2405 let messages = mode.build_messages("Content", &[], 0);
2406
2407 assert!(!messages[0].content.contains("iteration"));
2408 }
2409
2410 #[test]
2411 fn test_build_messages_iteration_one() {
2412 let config = create_test_config();
2413 let rt = tokio::runtime::Runtime::new().unwrap();
2414 let storage = rt.block_on(SqliteStorage::new_in_memory()).unwrap();
2415 let langbase =
2416 LangbaseClient::new(&config.langbase, crate::config::RequestConfig::default()).unwrap();
2417
2418 let mode = ReflectionMode::new(storage, langbase, &config);
2419 let messages = mode.build_messages("Content", &[], 1);
2420
2421 assert!(messages[0].content.contains("iteration 2"));
2422 }
2423}