1use serde::{Deserialize, Serialize};
10use std::time::Instant;
11use tracing::info;
12
13use super::ModeCore;
14use crate::config::Config;
15use crate::error::{AppResult, ToolError};
16use crate::langbase::{
17 BiasDetectionResponse, FallacyDetectionResponse, LangbaseClient, Message, PipeRequest,
18};
19use crate::prompts::{BIAS_DETECTION_PROMPT, FALLACY_DETECTION_PROMPT};
20use crate::storage::{Detection, DetectionType, SqliteStorage, Storage};
21
22#[derive(Debug, Clone, Deserialize)]
28pub struct DetectBiasesParams {
29 pub content: Option<String>,
31 pub thought_id: Option<String>,
33 pub session_id: Option<String>,
35 pub check_types: Option<Vec<String>>,
37}
38
39#[derive(Debug, Clone, Serialize)]
41pub struct DetectBiasesResult {
42 pub detections: Vec<Detection>,
44 pub detection_count: usize,
46 pub analyzed_content_length: usize,
48 pub overall_assessment: Option<String>,
50 pub reasoning_quality: Option<f64>,
52}
53
54#[derive(Debug, Clone, Deserialize)]
60pub struct DetectFallaciesParams {
61 pub content: Option<String>,
63 pub thought_id: Option<String>,
65 pub session_id: Option<String>,
67 #[serde(default = "default_true")]
69 pub check_formal: bool,
70 #[serde(default = "default_true")]
72 pub check_informal: bool,
73}
74
75fn default_true() -> bool {
76 true
77}
78
79#[derive(Debug, Clone, Serialize)]
81pub struct DetectFallaciesResult {
82 pub detections: Vec<Detection>,
84 pub detection_count: usize,
86 pub analyzed_content_length: usize,
88 pub overall_assessment: Option<String>,
90 pub argument_validity: Option<f64>,
92}
93
94#[derive(Clone)]
100pub struct DetectionMode {
101 core: ModeCore,
103 detection_pipe: String,
105}
106
107impl DetectionMode {
108 pub fn new(storage: SqliteStorage, langbase: LangbaseClient, config: &Config) -> Self {
110 let detection_pipe = config
111 .pipes
112 .detection
113 .as_ref()
114 .and_then(|d| d.pipe.clone())
115 .unwrap_or_else(|| "detection-v1".to_string());
116
117 info!(
118 pipe = %detection_pipe,
119 from_env = config.pipes.detection.as_ref().and_then(|d| d.pipe.as_ref()).is_some(),
120 "DetectionMode initialized with pipe"
121 );
122
123 Self {
124 core: ModeCore::new(storage, langbase),
125 detection_pipe,
126 }
127 }
128
129 pub async fn detect_biases(&self, params: DetectBiasesParams) -> AppResult<DetectBiasesResult> {
131 let start = Instant::now();
132
133 let (analysis_content, thought_id) = self
135 .resolve_content(
136 params.content.as_deref(),
137 params.thought_id.as_deref(),
138 "detect_biases",
139 )
140 .await?;
141
142 let mut messages = vec![Message::system(BIAS_DETECTION_PROMPT)];
144
145 if let Some(check_types) = ¶ms.check_types {
147 if !check_types.is_empty() {
148 messages.push(Message::user(format!(
149 "Focus specifically on detecting these bias types: {}\n\nContent to analyze:\n{}",
150 check_types.join(", "),
151 analysis_content
152 )));
153 } else {
154 messages.push(Message::user(format!(
155 "Analyze the following content for cognitive biases:\n\n{}",
156 analysis_content
157 )));
158 }
159 } else {
160 messages.push(Message::user(format!(
161 "Analyze the following content for cognitive biases:\n\n{}",
162 analysis_content
163 )));
164 }
165
166 let request = PipeRequest::new(&self.detection_pipe, messages);
168 let response = self.core.langbase().call_pipe(request).await?;
169
170 let bias_response = BiasDetectionResponse::from_completion(&response.completion);
172
173 let mut detections = Vec::new();
175 for detected in &bias_response.detections {
176 let mut detection = Detection::new(
177 DetectionType::Bias,
178 &detected.bias_type,
179 detected.severity,
180 detected.confidence,
181 &detected.explanation,
182 );
183
184 if let Some(session_id) = ¶ms.session_id {
185 detection = detection.with_session(session_id);
186 }
187 if let Some(tid) = &thought_id {
188 detection = detection.with_thought(tid);
189 }
190 if let Some(remediation) = &detected.remediation {
191 detection = detection.with_remediation(remediation);
192 }
193 if let Some(excerpt) = &detected.excerpt {
194 detection = detection.with_metadata(serde_json::json!({ "excerpt": excerpt }));
195 }
196
197 self.core.storage().create_detection(&detection).await?;
199 detections.push(detection);
200 }
201
202 let latency = start.elapsed().as_millis();
203 info!(
204 detection_count = detections.len(),
205 latency_ms = latency,
206 "Bias detection completed"
207 );
208
209 Ok(DetectBiasesResult {
210 detections,
211 detection_count: bias_response.detections.len(),
212 analyzed_content_length: analysis_content.len(),
213 overall_assessment: Some(bias_response.overall_assessment),
214 reasoning_quality: Some(bias_response.reasoning_quality),
215 })
216 }
217
218 pub async fn detect_fallacies(
220 &self,
221 params: DetectFallaciesParams,
222 ) -> AppResult<DetectFallaciesResult> {
223 let start = Instant::now();
224
225 if !params.check_formal && !params.check_informal {
227 return Err(ToolError::Validation {
228 field: "check_formal/check_informal".to_string(),
229 reason: "At least one of check_formal or check_informal must be true".to_string(),
230 }
231 .into());
232 }
233
234 let (analysis_content, thought_id) = self
236 .resolve_content(
237 params.content.as_deref(),
238 params.thought_id.as_deref(),
239 "detect_fallacies",
240 )
241 .await?;
242
243 info!(
244 check_formal = %params.check_formal,
245 check_informal = %params.check_informal,
246 "Detecting fallacies"
247 );
248
249 let mut messages = vec![Message::system(FALLACY_DETECTION_PROMPT)];
251
252 let check_instruction = match (params.check_formal, params.check_informal) {
254 (true, true) => "Check for both formal and informal logical fallacies.",
255 (true, false) => "Focus only on formal logical fallacies (structural errors).",
256 (false, true) => "Focus only on informal logical fallacies (content/context errors).",
257 (false, false) => unreachable!(), };
259
260 messages.push(Message::user(format!(
261 "{}\n\nContent to analyze:\n{}",
262 check_instruction, analysis_content
263 )));
264
265 let request = PipeRequest::new(&self.detection_pipe, messages);
267 let response = self.core.langbase().call_pipe(request).await?;
268
269 let fallacy_response = FallacyDetectionResponse::from_completion(&response.completion);
271
272 let mut detections = Vec::new();
274 for detected in &fallacy_response.detections {
275 let is_formal = detected.category.to_lowercase() == "formal";
277 if (is_formal && !params.check_formal) || (!is_formal && !params.check_informal) {
278 continue;
279 }
280
281 let mut detection = Detection::new(
282 DetectionType::Fallacy,
283 &detected.fallacy_type,
284 detected.severity,
285 detected.confidence,
286 &detected.explanation,
287 );
288
289 if let Some(session_id) = ¶ms.session_id {
290 detection = detection.with_session(session_id);
291 }
292 if let Some(tid) = &thought_id {
293 detection = detection.with_thought(tid);
294 }
295 if let Some(remediation) = &detected.remediation {
296 detection = detection.with_remediation(remediation);
297 }
298
299 let mut meta = serde_json::Map::new();
301 meta.insert("category".to_string(), serde_json::json!(detected.category));
302 if let Some(excerpt) = &detected.excerpt {
303 meta.insert("excerpt".to_string(), serde_json::json!(excerpt));
304 }
305 detection = detection.with_metadata(serde_json::Value::Object(meta));
306
307 self.core.storage().create_detection(&detection).await?;
309 detections.push(detection);
310 }
311
312 let latency = start.elapsed().as_millis();
313 info!(
314 detection_count = detections.len(),
315 latency_ms = latency,
316 "Fallacy detection completed"
317 );
318
319 Ok(DetectFallaciesResult {
320 detections,
321 detection_count: fallacy_response.detections.len(),
322 analyzed_content_length: analysis_content.len(),
323 overall_assessment: Some(fallacy_response.overall_assessment),
324 argument_validity: Some(fallacy_response.argument_validity),
325 })
326 }
327
328 async fn resolve_content(
330 &self,
331 content: Option<&str>,
332 thought_id: Option<&str>,
333 operation: &str,
334 ) -> AppResult<(String, Option<String>)> {
335 match (content, thought_id) {
336 (Some(content), _) => Ok((content.to_string(), thought_id.map(|s| s.to_string()))),
337 (None, Some(thought_id)) => {
338 let thought = self
339 .core
340 .storage()
341 .get_thought(thought_id)
342 .await?
343 .ok_or_else(|| {
344 ToolError::Session(format!("Thought not found: {}", thought_id))
345 })?;
346 Ok((thought.content, Some(thought_id.to_string())))
347 }
348 (None, None) => Err(ToolError::Validation {
349 field: "content/thought_id".to_string(),
350 reason: format!(
351 "Either 'content' or 'thought_id' must be provided for {}",
352 operation
353 ),
354 }
355 .into()),
356 }
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn test_detect_biases_params_deserialize() {
366 let json = r#"{"content": "Test content"}"#;
367 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
368 assert_eq!(params.content, Some("Test content".to_string()));
369 assert!(params.thought_id.is_none());
370 assert!(params.session_id.is_none());
371 assert!(params.check_types.is_none());
372 }
373
374 #[test]
375 fn test_detect_biases_params_with_check_types() {
376 let json = r#"{"content": "Test", "check_types": ["confirmation_bias", "anchoring"]}"#;
377 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
378 assert_eq!(
379 params.check_types,
380 Some(vec![
381 "confirmation_bias".to_string(),
382 "anchoring".to_string()
383 ])
384 );
385 }
386
387 #[test]
388 fn test_detect_fallacies_params_defaults() {
389 let json = r#"{"content": "Test content"}"#;
390 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
391 assert!(params.check_formal);
392 assert!(params.check_informal);
393 }
394
395 #[test]
396 fn test_detect_fallacies_params_custom_checks() {
397 let json = r#"{"content": "Test", "check_formal": false, "check_informal": true}"#;
398 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
399 assert!(!params.check_formal);
400 assert!(params.check_informal);
401 }
402
403 #[test]
404 fn test_detect_biases_result_serialize() {
405 let result = DetectBiasesResult {
406 detections: vec![],
407 detection_count: 0,
408 analyzed_content_length: 100,
409 overall_assessment: Some("Good reasoning".to_string()),
410 reasoning_quality: Some(0.85),
411 };
412 let json = serde_json::to_string(&result).unwrap();
413 assert!(json.contains("detection_count"));
414 assert!(json.contains("0.85"));
415 }
416
417 #[test]
418 fn test_detect_fallacies_result_serialize() {
419 let result = DetectFallaciesResult {
420 detections: vec![],
421 detection_count: 2,
422 analyzed_content_length: 200,
423 overall_assessment: Some("Some issues found".to_string()),
424 argument_validity: Some(0.7),
425 };
426 let json = serde_json::to_string(&result).unwrap();
427 assert!(json.contains("detection_count"));
428 assert!(json.contains("0.7"));
429 }
430
431 #[test]
432 fn test_default_true_function() {
433 assert!(default_true());
434 }
435
436 #[test]
437 fn test_detect_biases_params_all_fields() {
438 let json = r#"{
439 "content": "Test content",
440 "thought_id": "thought-123",
441 "session_id": "session-456",
442 "check_types": ["confirmation_bias"]
443 }"#;
444 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
445 assert_eq!(params.content, Some("Test content".to_string()));
446 assert_eq!(params.thought_id, Some("thought-123".to_string()));
447 assert_eq!(params.session_id, Some("session-456".to_string()));
448 assert_eq!(
449 params.check_types,
450 Some(vec!["confirmation_bias".to_string()])
451 );
452 }
453
454 #[test]
455 fn test_detect_biases_params_empty_check_types() {
456 let json = r#"{"content": "Test", "check_types": []}"#;
457 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
458 assert_eq!(params.check_types, Some(vec![]));
459 }
460
461 #[test]
462 fn test_detect_biases_params_only_thought_id() {
463 let json = r#"{"thought_id": "thought-789"}"#;
464 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
465 assert!(params.content.is_none());
466 assert_eq!(params.thought_id, Some("thought-789".to_string()));
467 assert!(params.session_id.is_none());
468 assert!(params.check_types.is_none());
469 }
470
471 #[test]
472 fn test_detect_fallacies_params_all_fields() {
473 let json = r#"{
474 "content": "Test content",
475 "thought_id": "thought-123",
476 "session_id": "session-456",
477 "check_formal": true,
478 "check_informal": false
479 }"#;
480 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
481 assert_eq!(params.content, Some("Test content".to_string()));
482 assert_eq!(params.thought_id, Some("thought-123".to_string()));
483 assert_eq!(params.session_id, Some("session-456".to_string()));
484 assert!(params.check_formal);
485 assert!(!params.check_informal);
486 }
487
488 #[test]
489 fn test_detect_fallacies_params_only_thought_id() {
490 let json = r#"{"thought_id": "thought-789"}"#;
491 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
492 assert!(params.content.is_none());
493 assert_eq!(params.thought_id, Some("thought-789".to_string()));
494 assert!(params.session_id.is_none());
495 assert!(params.check_formal); assert!(params.check_informal); }
498
499 #[test]
500 fn test_detect_biases_result_round_trip() {
501 let original = DetectBiasesResult {
502 detections: vec![],
503 detection_count: 3,
504 analyzed_content_length: 250,
505 overall_assessment: Some("Multiple biases detected".to_string()),
506 reasoning_quality: Some(0.65),
507 };
508 let json = serde_json::to_string(&original).unwrap();
509 let deserialized: serde_json::Value = serde_json::from_str(&json).unwrap();
510
511 assert_eq!(deserialized["detection_count"], 3);
512 assert_eq!(deserialized["analyzed_content_length"], 250);
513 assert_eq!(deserialized["reasoning_quality"], 0.65);
514 }
515
516 #[test]
517 fn test_detect_fallacies_result_round_trip() {
518 let original = DetectFallaciesResult {
519 detections: vec![],
520 detection_count: 1,
521 analyzed_content_length: 150,
522 overall_assessment: Some("One fallacy detected".to_string()),
523 argument_validity: Some(0.9),
524 };
525 let json = serde_json::to_string(&original).unwrap();
526 let deserialized: serde_json::Value = serde_json::from_str(&json).unwrap();
527
528 assert_eq!(deserialized["detection_count"], 1);
529 assert_eq!(deserialized["analyzed_content_length"], 150);
530 assert_eq!(deserialized["argument_validity"], 0.9);
531 }
532
533 #[test]
534 fn test_detect_biases_result_with_none_values() {
535 let result = DetectBiasesResult {
536 detections: vec![],
537 detection_count: 0,
538 analyzed_content_length: 50,
539 overall_assessment: None,
540 reasoning_quality: None,
541 };
542 let json = serde_json::to_string(&result).unwrap();
543 assert!(json.contains("detection_count"));
544 assert!(json.contains("\"overall_assessment\":null"));
545 assert!(json.contains("\"reasoning_quality\":null"));
546 }
547
548 #[test]
549 fn test_detect_fallacies_result_with_none_values() {
550 let result = DetectFallaciesResult {
551 detections: vec![],
552 detection_count: 0,
553 analyzed_content_length: 75,
554 overall_assessment: None,
555 argument_validity: None,
556 };
557 let json = serde_json::to_string(&result).unwrap();
558 assert!(json.contains("detection_count"));
559 assert!(json.contains("\"overall_assessment\":null"));
560 assert!(json.contains("\"argument_validity\":null"));
561 }
562
563 #[test]
568 fn test_detect_biases_params_empty_content() {
569 let json = r#"{"content": ""}"#;
570 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
571 assert_eq!(params.content, Some("".to_string()));
572 }
573
574 #[test]
575 fn test_detect_biases_params_unicode_content() {
576 let json = r#"{"content": "测试 🎉 тест"}"#;
577 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
578 assert_eq!(params.content, Some("测试 🎉 тест".to_string()));
579 }
580
581 #[test]
582 fn test_detect_biases_params_long_content() {
583 let long_text = "a".repeat(10000);
584 let json = serde_json::json!({
585 "content": long_text
586 });
587 let params: DetectBiasesParams = serde_json::from_value(json).unwrap();
588 assert_eq!(params.content.unwrap().len(), 10000);
589 }
590
591 #[test]
592 fn test_detect_biases_params_special_characters() {
593 let json = r#"{"content": "Line1\nLine2\t\"quoted\""}"#;
594 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
595 let content = params.content.unwrap();
596 assert!(content.contains("Line1"));
597 assert!(content.contains("Line2"));
598 }
599
600 #[test]
601 fn test_detect_fallacies_params_empty_content() {
602 let json = r#"{"content": ""}"#;
603 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
604 assert_eq!(params.content, Some("".to_string()));
605 }
606
607 #[test]
608 fn test_detect_fallacies_params_unicode_content() {
609 let json = r#"{"content": "Test 日本語"}"#;
610 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
611 assert_eq!(params.content, Some("Test 日本語".to_string()));
612 }
613
614 #[test]
615 fn test_detect_biases_result_boundary_scores() {
616 let result = DetectBiasesResult {
617 detections: vec![],
618 detection_count: 0,
619 analyzed_content_length: 0,
620 overall_assessment: Some("".to_string()),
621 reasoning_quality: Some(0.0),
622 };
623 let json = serde_json::to_string(&result).unwrap();
624 assert!(json.contains("0.0"));
625
626 let result = DetectBiasesResult {
627 detections: vec![],
628 detection_count: 999,
629 analyzed_content_length: 999999,
630 overall_assessment: Some("Max".to_string()),
631 reasoning_quality: Some(1.0),
632 };
633 let json = serde_json::to_string(&result).unwrap();
634 assert!(json.contains("1.0"));
635 assert!(json.contains("999999"));
636 }
637
638 #[test]
639 fn test_detect_fallacies_result_boundary_scores() {
640 let result = DetectFallaciesResult {
641 detections: vec![],
642 detection_count: 0,
643 analyzed_content_length: 0,
644 overall_assessment: Some("".to_string()),
645 argument_validity: Some(0.0),
646 };
647 let json = serde_json::to_string(&result).unwrap();
648 assert!(json.contains("0.0"));
649
650 let result = DetectFallaciesResult {
651 detections: vec![],
652 detection_count: 500,
653 analyzed_content_length: 100000,
654 overall_assessment: Some("Max".to_string()),
655 argument_validity: Some(1.0),
656 };
657 let json = serde_json::to_string(&result).unwrap();
658 assert!(json.contains("1.0"));
659 assert!(json.contains("100000"));
660 }
661
662 #[test]
667 fn test_detect_biases_params_clone() {
668 let params = DetectBiasesParams {
669 content: Some("Test".to_string()),
670 thought_id: Some("t1".to_string()),
671 session_id: Some("s1".to_string()),
672 check_types: Some(vec!["bias1".to_string()]),
673 };
674 let cloned = params.clone();
675 assert_eq!(params.content, cloned.content);
676 assert_eq!(params.thought_id, cloned.thought_id);
677 assert_eq!(params.session_id, cloned.session_id);
678 assert_eq!(params.check_types, cloned.check_types);
679 }
680
681 #[test]
682 fn test_detect_fallacies_params_clone() {
683 let params = DetectFallaciesParams {
684 content: Some("Test".to_string()),
685 thought_id: Some("t1".to_string()),
686 session_id: Some("s1".to_string()),
687 check_formal: true,
688 check_informal: false,
689 };
690 let cloned = params.clone();
691 assert_eq!(params.content, cloned.content);
692 assert_eq!(params.check_formal, cloned.check_formal);
693 assert_eq!(params.check_informal, cloned.check_informal);
694 }
695
696 #[test]
697 fn test_detect_biases_result_clone() {
698 let result = DetectBiasesResult {
699 detections: vec![],
700 detection_count: 5,
701 analyzed_content_length: 100,
702 overall_assessment: Some("Test".to_string()),
703 reasoning_quality: Some(0.75),
704 };
705 let cloned = result.clone();
706 assert_eq!(result.detection_count, cloned.detection_count);
707 assert_eq!(
708 result.analyzed_content_length,
709 cloned.analyzed_content_length
710 );
711 assert_eq!(result.reasoning_quality, cloned.reasoning_quality);
712 }
713
714 #[test]
715 fn test_detect_fallacies_result_clone() {
716 let result = DetectFallaciesResult {
717 detections: vec![],
718 detection_count: 3,
719 analyzed_content_length: 200,
720 overall_assessment: Some("Test".to_string()),
721 argument_validity: Some(0.8),
722 };
723 let cloned = result.clone();
724 assert_eq!(result.detection_count, cloned.detection_count);
725 assert_eq!(result.argument_validity, cloned.argument_validity);
726 }
727
728 #[test]
729 fn test_detect_biases_params_debug() {
730 let params = DetectBiasesParams {
731 content: Some("Test".to_string()),
732 thought_id: None,
733 session_id: None,
734 check_types: None,
735 };
736 let debug_str = format!("{:?}", params);
737 assert!(debug_str.contains("DetectBiasesParams"));
738 assert!(debug_str.contains("Test"));
739 }
740
741 #[test]
742 fn test_detect_fallacies_params_debug() {
743 let params = DetectFallaciesParams {
744 content: Some("Test".to_string()),
745 thought_id: None,
746 session_id: None,
747 check_formal: true,
748 check_informal: true,
749 };
750 let debug_str = format!("{:?}", params);
751 assert!(debug_str.contains("DetectFallaciesParams"));
752 assert!(debug_str.contains("Test"));
753 }
754
755 #[test]
756 fn test_detect_biases_result_debug() {
757 let result = DetectBiasesResult {
758 detections: vec![],
759 detection_count: 1,
760 analyzed_content_length: 50,
761 overall_assessment: Some("Test".to_string()),
762 reasoning_quality: Some(0.9),
763 };
764 let debug_str = format!("{:?}", result);
765 assert!(debug_str.contains("DetectBiasesResult"));
766 }
767
768 #[test]
769 fn test_detect_fallacies_result_debug() {
770 let result = DetectFallaciesResult {
771 detections: vec![],
772 detection_count: 2,
773 analyzed_content_length: 100,
774 overall_assessment: Some("Test".to_string()),
775 argument_validity: Some(0.85),
776 };
777 let debug_str = format!("{:?}", result);
778 assert!(debug_str.contains("DetectFallaciesResult"));
779 }
780
781 #[test]
786 fn test_detect_biases_params_invalid_json() {
787 let json = r#"{"content": "Test", invalid}"#;
788 let result: Result<DetectBiasesParams, _> = serde_json::from_str(json);
789 assert!(result.is_err());
790 }
791
792 #[test]
793 fn test_detect_fallacies_params_invalid_json() {
794 let json = r#"{"content": malformed"#;
795 let result: Result<DetectFallaciesParams, _> = serde_json::from_str(json);
796 assert!(result.is_err());
797 }
798
799 #[test]
800 fn test_detect_biases_params_missing_all_fields() {
801 let json = r#"{}"#;
802 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
803 assert!(params.content.is_none());
804 assert!(params.thought_id.is_none());
805 assert!(params.session_id.is_none());
806 assert!(params.check_types.is_none());
807 }
808
809 #[test]
810 fn test_detect_fallacies_params_missing_all_optional_fields() {
811 let json = r#"{}"#;
812 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
813 assert!(params.content.is_none());
814 assert!(params.thought_id.is_none());
815 assert!(params.session_id.is_none());
816 assert!(params.check_formal); assert!(params.check_informal); }
819
820 #[test]
821 fn test_detect_biases_params_null_values() {
822 let json =
823 r#"{"content": null, "thought_id": null, "session_id": null, "check_types": null}"#;
824 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
825 assert!(params.content.is_none());
826 assert!(params.thought_id.is_none());
827 assert!(params.session_id.is_none());
828 assert!(params.check_types.is_none());
829 }
830
831 #[test]
832 fn test_detect_fallacies_params_null_values() {
833 let json = r#"{"content": null, "thought_id": null, "session_id": null}"#;
834 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
835 assert!(params.content.is_none());
836 assert!(params.thought_id.is_none());
837 assert!(params.session_id.is_none());
838 }
839
840 #[test]
845 fn test_detect_biases_params_single_check_type() {
846 let json = r#"{"content": "Test", "check_types": ["anchoring"]}"#;
847 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
848 assert_eq!(params.check_types, Some(vec!["anchoring".to_string()]));
849 }
850
851 #[test]
852 fn test_detect_biases_params_many_check_types() {
853 let json =
854 r#"{"content": "Test", "check_types": ["bias1", "bias2", "bias3", "bias4", "bias5"]}"#;
855 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
856 assert_eq!(params.check_types.as_ref().unwrap().len(), 5);
857 }
858
859 #[test]
860 fn test_detect_biases_params_check_types_with_spaces() {
861 let json = r#"{"content": "Test", "check_types": ["confirmation bias", "anchoring bias"]}"#;
862 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
863 assert_eq!(
864 params.check_types,
865 Some(vec![
866 "confirmation bias".to_string(),
867 "anchoring bias".to_string()
868 ])
869 );
870 }
871
872 #[test]
877 fn test_detect_biases_result_serialization_field_names() {
878 let result = DetectBiasesResult {
879 detections: vec![],
880 detection_count: 1,
881 analyzed_content_length: 100,
882 overall_assessment: Some("Test".to_string()),
883 reasoning_quality: Some(0.5),
884 };
885 let json = serde_json::to_string(&result).unwrap();
886 assert!(json.contains("detections"));
887 assert!(json.contains("detection_count"));
888 assert!(json.contains("analyzed_content_length"));
889 assert!(json.contains("overall_assessment"));
890 assert!(json.contains("reasoning_quality"));
891 }
892
893 #[test]
894 fn test_detect_fallacies_result_serialization_field_names() {
895 let result = DetectFallaciesResult {
896 detections: vec![],
897 detection_count: 1,
898 analyzed_content_length: 100,
899 overall_assessment: Some("Test".to_string()),
900 argument_validity: Some(0.5),
901 };
902 let json = serde_json::to_string(&result).unwrap();
903 assert!(json.contains("detections"));
904 assert!(json.contains("detection_count"));
905 assert!(json.contains("analyzed_content_length"));
906 assert!(json.contains("overall_assessment"));
907 assert!(json.contains("argument_validity"));
908 }
909
910 #[test]
915 fn test_detect_fallacies_params_both_false() {
916 let json = r#"{"content": "Test", "check_formal": false, "check_informal": false}"#;
917 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
918 assert!(!params.check_formal);
919 assert!(!params.check_informal);
920 }
921
922 #[test]
923 fn test_detect_fallacies_params_only_formal_true() {
924 let json = r#"{"content": "Test", "check_formal": true, "check_informal": false}"#;
925 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
926 assert!(params.check_formal);
927 assert!(!params.check_informal);
928 }
929
930 #[test]
931 fn test_detect_fallacies_params_only_informal_true() {
932 let json = r#"{"content": "Test", "check_formal": false, "check_informal": true}"#;
933 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
934 assert!(!params.check_formal);
935 assert!(params.check_informal);
936 }
937
938 #[test]
939 fn test_detect_fallacies_params_both_true() {
940 let json = r#"{"content": "Test", "check_formal": true, "check_informal": true}"#;
941 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
942 assert!(params.check_formal);
943 assert!(params.check_informal);
944 }
945
946 #[test]
951 fn test_detect_biases_result_large_detection_count() {
952 let result = DetectBiasesResult {
953 detections: vec![],
954 detection_count: 1000,
955 analyzed_content_length: 50000,
956 overall_assessment: Some("Many detections".to_string()),
957 reasoning_quality: Some(0.3),
958 };
959 let json = serde_json::to_string(&result).unwrap();
960 assert!(json.contains("1000"));
961 assert!(json.contains("50000"));
962 }
963
964 #[test]
965 fn test_detect_fallacies_result_large_detection_count() {
966 let result = DetectFallaciesResult {
967 detections: vec![],
968 detection_count: 2000,
969 analyzed_content_length: 100000,
970 overall_assessment: Some("Critical issues".to_string()),
971 argument_validity: Some(0.1),
972 };
973 let json = serde_json::to_string(&result).unwrap();
974 assert!(json.contains("2000"));
975 assert!(json.contains("100000"));
976 }
977
978 #[test]
983 fn test_detect_biases_result_float_precision() {
984 let result = DetectBiasesResult {
985 detections: vec![],
986 detection_count: 1,
987 analyzed_content_length: 100,
988 overall_assessment: Some("Test".to_string()),
989 reasoning_quality: Some(0.123456789),
990 };
991 let json = serde_json::to_string(&result).unwrap();
992 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
993 let quality = parsed["reasoning_quality"].as_f64().unwrap();
994 assert!((quality - 0.123456789).abs() < 1e-9);
995 }
996
997 #[test]
998 fn test_detect_fallacies_result_float_precision() {
999 let result = DetectFallaciesResult {
1000 detections: vec![],
1001 detection_count: 1,
1002 analyzed_content_length: 100,
1003 overall_assessment: Some("Test".to_string()),
1004 argument_validity: Some(0.987654321),
1005 };
1006 let json = serde_json::to_string(&result).unwrap();
1007 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1008 let validity = parsed["argument_validity"].as_f64().unwrap();
1009 assert!((validity - 0.987654321).abs() < 1e-9);
1010 }
1011
1012 #[test]
1017 fn test_default_true_is_consistent() {
1018 assert!(default_true());
1019 assert_eq!(default_true(), default_true());
1020 }
1021
1022 #[test]
1027 fn test_detect_biases_params_empty_string_fields() {
1028 let json = r#"{"content": "", "thought_id": "", "session_id": ""}"#;
1029 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
1030 assert_eq!(params.content, Some("".to_string()));
1031 assert_eq!(params.thought_id, Some("".to_string()));
1032 assert_eq!(params.session_id, Some("".to_string()));
1033 }
1034
1035 #[test]
1036 fn test_detect_fallacies_params_empty_string_fields() {
1037 let json = r#"{"content": "", "thought_id": "", "session_id": ""}"#;
1038 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
1039 assert_eq!(params.content, Some("".to_string()));
1040 assert_eq!(params.thought_id, Some("".to_string()));
1041 assert_eq!(params.session_id, Some("".to_string()));
1042 }
1043
1044 #[test]
1045 fn test_detect_biases_result_empty_assessment() {
1046 let result = DetectBiasesResult {
1047 detections: vec![],
1048 detection_count: 0,
1049 analyzed_content_length: 0,
1050 overall_assessment: Some("".to_string()),
1051 reasoning_quality: Some(0.5),
1052 };
1053 let json = serde_json::to_string(&result).unwrap();
1054 assert!(json.contains(r#""overall_assessment":"""#));
1055 }
1056
1057 #[test]
1058 fn test_detect_fallacies_result_empty_assessment() {
1059 let result = DetectFallaciesResult {
1060 detections: vec![],
1061 detection_count: 0,
1062 analyzed_content_length: 0,
1063 overall_assessment: Some("".to_string()),
1064 argument_validity: Some(0.5),
1065 };
1066 let json = serde_json::to_string(&result).unwrap();
1067 assert!(json.contains(r#""overall_assessment":"""#));
1068 }
1069
1070 #[test]
1075 fn test_detect_biases_params_mixed_some_none() {
1076 let json =
1077 r#"{"content": "Test", "thought_id": null, "session_id": "s1", "check_types": null}"#;
1078 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
1079 assert_eq!(params.content, Some("Test".to_string()));
1080 assert!(params.thought_id.is_none());
1081 assert_eq!(params.session_id, Some("s1".to_string()));
1082 assert!(params.check_types.is_none());
1083 }
1084
1085 #[test]
1086 fn test_detect_fallacies_params_mixed_some_none() {
1087 let json = r#"{"content": null, "thought_id": "t1", "session_id": null}"#;
1088 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
1089 assert!(params.content.is_none());
1090 assert_eq!(params.thought_id, Some("t1".to_string()));
1091 assert!(params.session_id.is_none());
1092 }
1093
1094 #[test]
1099 fn test_detect_biases_result_negative_score() {
1100 let result = DetectBiasesResult {
1101 detections: vec![],
1102 detection_count: 0,
1103 analyzed_content_length: 0,
1104 overall_assessment: None,
1105 reasoning_quality: Some(-0.5),
1106 };
1107 let json = serde_json::to_string(&result).unwrap();
1108 assert!(json.contains("-0.5"));
1109 }
1110
1111 #[test]
1112 fn test_detect_fallacies_result_over_one_score() {
1113 let result = DetectFallaciesResult {
1114 detections: vec![],
1115 detection_count: 0,
1116 analyzed_content_length: 0,
1117 overall_assessment: None,
1118 argument_validity: Some(1.5),
1119 };
1120 let json = serde_json::to_string(&result).unwrap();
1121 assert!(json.contains("1.5"));
1122 }
1123
1124 #[test]
1129 fn test_detect_biases_result_very_long_assessment() {
1130 let long_text = "a".repeat(50000);
1131 let result = DetectBiasesResult {
1132 detections: vec![],
1133 detection_count: 0,
1134 analyzed_content_length: 50000,
1135 overall_assessment: Some(long_text),
1136 reasoning_quality: Some(0.5),
1137 };
1138 let json = serde_json::to_string(&result).unwrap();
1139 assert!(json.len() > 50000);
1140 }
1141
1142 #[test]
1143 fn test_detect_fallacies_result_very_long_assessment() {
1144 let long_text = "b".repeat(30000);
1145 let result = DetectFallaciesResult {
1146 detections: vec![],
1147 detection_count: 0,
1148 analyzed_content_length: 30000,
1149 overall_assessment: Some(long_text),
1150 argument_validity: Some(0.5),
1151 };
1152 let json = serde_json::to_string(&result).unwrap();
1153 assert!(json.len() > 30000);
1154 }
1155
1156 #[test]
1161 fn test_detect_biases_params_whitespace_content() {
1162 let json = r#"{"content": " \n\t "}"#;
1163 let params: DetectBiasesParams = serde_json::from_str(json).unwrap();
1164 assert_eq!(params.content, Some(" \n\t ".to_string()));
1165 }
1166
1167 #[test]
1168 fn test_detect_fallacies_params_whitespace_content() {
1169 let json = r#"{"content": "\t\n\r "}"#;
1170 let params: DetectFallaciesParams = serde_json::from_str(json).unwrap();
1171 assert_eq!(params.content, Some("\t\n\r ".to_string()));
1172 }
1173
1174 #[test]
1179 fn test_detect_biases_params_wrong_type_check_types() {
1180 let json = r#"{"content": "Test", "check_types": "not_an_array"}"#;
1181 let result: Result<DetectBiasesParams, _> = serde_json::from_str(json);
1182 assert!(result.is_err());
1183 }
1184
1185 #[test]
1186 fn test_detect_fallacies_params_wrong_type_check_formal() {
1187 let json = r#"{"content": "Test", "check_formal": "true"}"#;
1188 let result: Result<DetectFallaciesParams, _> = serde_json::from_str(json);
1189 assert!(result.is_err());
1190 }
1191
1192 #[test]
1193 fn test_detect_biases_result_wrong_type_detection_count() {
1194 let json = r#"{"detections": [], "detection_count": "5", "analyzed_content_length": 100}"#;
1195 let result: Result<serde_json::Value, _> = serde_json::from_str(json);
1196 assert!(result.is_ok()); }
1198}