1use std::future::Future;
43use std::pin::Pin;
44
45use serde::{Deserialize, Serialize};
46use serde_json::json;
47use thiserror::Error;
48
49use super::{ThinkToolContext, ThinkToolModule, ThinkToolModuleConfig, ThinkToolOutput};
50use crate::error::{Error, Result};
51
52#[derive(Error, Debug, Clone)]
58pub enum GigaThinkError {
59 #[error("Insufficient perspectives: generated {generated}, required minimum {required}")]
61 InsufficientPerspectives { generated: usize, required: usize },
62
63 #[error("Invalid analysis dimension: {dimension}")]
65 InvalidDimension { dimension: String },
66
67 #[error("Query too short: {length} characters, minimum required is {minimum}")]
69 QueryTooShort { length: usize, minimum: usize },
70
71 #[error("Query too long: {length} characters, maximum allowed is {maximum}")]
73 QueryTooLong { length: usize, maximum: usize },
74
75 #[error("Confidence too low: {confidence:.2}, minimum required is {threshold:.2}")]
77 LowConfidence { confidence: f64, threshold: f64 },
78
79 #[error("Cross-validation failed: {reason}")]
81 CrossValidationFailed { reason: String },
82
83 #[error("Failed to synthesize perspectives: {reason}")]
85 SynthesisFailed { reason: String },
86
87 #[error("Execution timeout after {duration_ms}ms")]
89 ExecutionTimeout { duration_ms: u64 },
90}
91
92impl From<GigaThinkError> for Error {
93 fn from(err: GigaThinkError) -> Self {
94 Error::ThinkToolExecutionError(err.to_string())
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct GigaThinkConfig {
105 pub min_perspectives: usize,
107
108 pub max_perspectives: usize,
110
111 pub min_confidence: f64,
113
114 pub enable_cross_validation: bool,
116
117 pub min_query_length: usize,
119
120 pub max_query_length: usize,
122
123 pub dimensions: Vec<AnalysisDimension>,
125
126 pub novelty_weight: f64,
128
129 pub depth_weight: f64,
131
132 pub coherence_weight: f64,
134
135 pub max_execution_time_ms: Option<u64>,
137}
138
139impl Default for GigaThinkConfig {
140 fn default() -> Self {
141 Self {
142 min_perspectives: 10,
143 max_perspectives: 15,
144 min_confidence: 0.70,
145 enable_cross_validation: true,
146 min_query_length: 10,
147 max_query_length: 5000,
148 dimensions: Vec::new(), novelty_weight: 0.30,
150 depth_weight: 0.40,
151 coherence_weight: 0.30,
152 max_execution_time_ms: Some(10000),
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
163#[serde(rename_all = "snake_case")]
164pub enum AnalysisDimension {
165 Economic,
167 Technological,
169 Social,
171 Environmental,
173 Political,
175 Psychological,
177 Ethical,
179 Historical,
181 Competitive,
183 UserExperience,
185 RiskOpportunity,
187 Strategic,
189}
190
191impl AnalysisDimension {
192 pub fn all() -> Vec<Self> {
194 vec![
195 Self::Economic,
196 Self::Technological,
197 Self::Social,
198 Self::Environmental,
199 Self::Political,
200 Self::Psychological,
201 Self::Ethical,
202 Self::Historical,
203 Self::Competitive,
204 Self::UserExperience,
205 Self::RiskOpportunity,
206 Self::Strategic,
207 ]
208 }
209
210 pub fn display_name(&self) -> &'static str {
212 match self {
213 Self::Economic => "Economic/Financial",
214 Self::Technological => "Technological/Innovation",
215 Self::Social => "Social/Cultural",
216 Self::Environmental => "Environmental/Sustainability",
217 Self::Political => "Political/Regulatory",
218 Self::Psychological => "Psychological/Behavioral",
219 Self::Ethical => "Ethical/Moral",
220 Self::Historical => "Historical/Evolutionary",
221 Self::Competitive => "Competitive/Market",
222 Self::UserExperience => "User Experience/Adoption",
223 Self::RiskOpportunity => "Risk/Opportunity",
224 Self::Strategic => "Long-term/Strategic",
225 }
226 }
227
228 pub fn guiding_questions(&self) -> Vec<&'static str> {
230 match self {
231 Self::Economic => vec![
232 "What are the financial implications?",
233 "How does this affect costs and revenues?",
234 "What economic forces are at play?",
235 ],
236 Self::Technological => vec![
237 "What technologies enable or constrain this?",
238 "How might technology evolve to change this?",
239 "What innovation opportunities exist?",
240 ],
241 Self::Social => vec![
242 "How does society perceive this?",
243 "What cultural factors influence adoption?",
244 "Who are the key stakeholders affected?",
245 ],
246 Self::Environmental => vec![
247 "What is the environmental impact?",
248 "How sustainable is this approach?",
249 "What ecological factors are relevant?",
250 ],
251 Self::Political => vec![
252 "What regulations apply or might apply?",
253 "How do political dynamics affect this?",
254 "What policy changes could impact this?",
255 ],
256 Self::Psychological => vec![
257 "What cognitive biases might be at play?",
258 "How do people emotionally respond?",
259 "What behavioral patterns are relevant?",
260 ],
261 Self::Ethical => vec![
262 "What are the moral implications?",
263 "Who might be harmed or helped?",
264 "What ethical principles apply?",
265 ],
266 Self::Historical => vec![
267 "What historical precedents exist?",
268 "How has this evolved over time?",
269 "What can we learn from the past?",
270 ],
271 Self::Competitive => vec![
272 "Who are the competitors?",
273 "What are the market dynamics?",
274 "How do switching costs affect this?",
275 ],
276 Self::UserExperience => vec![
277 "How does this affect the user?",
278 "What friction points exist?",
279 "How can adoption be improved?",
280 ],
281 Self::RiskOpportunity => vec![
282 "What are the key risks?",
283 "What opportunities might emerge?",
284 "How can risks be mitigated?",
285 ],
286 Self::Strategic => vec![
287 "What is the long-term impact?",
288 "How does this fit into larger goals?",
289 "What strategic options exist?",
290 ],
291 }
292 }
293
294 pub fn prompt_template(&self) -> &'static str {
296 match self {
297 Self::Economic => {
298 "Analyze the economic and financial aspects. Consider costs, benefits, \
299 market forces, pricing dynamics, and value creation potential."
300 }
301 Self::Technological => {
302 "Examine the technological dimensions. Consider enabling technologies, \
303 technical constraints, innovation potential, and technical debt."
304 }
305 Self::Social => {
306 "Explore the social and cultural factors. Consider stakeholder interests, \
307 social norms, cultural adoption barriers, and community impact."
308 }
309 Self::Environmental => {
310 "Assess environmental implications. Consider sustainability, ecological \
311 footprint, resource consumption, and environmental regulations."
312 }
313 Self::Political => {
314 "Analyze the political and regulatory landscape. Consider current \
315 regulations, potential policy changes, and political stakeholders."
316 }
317 Self::Psychological => {
318 "Examine psychological and behavioral factors. Consider cognitive biases, \
319 emotional responses, habit formation, and decision-making patterns."
320 }
321 Self::Ethical => {
322 "Evaluate ethical and moral dimensions. Consider fairness, transparency, \
323 potential harms, beneficiaries, and alignment with ethical principles."
324 }
325 Self::Historical => {
326 "Review historical context and precedents. Consider how similar situations \
327 evolved, lessons learned, and historical patterns that might repeat."
328 }
329 Self::Competitive => {
330 "Analyze competitive dynamics. Consider existing competitors, potential \
331 entrants, substitute solutions, and market positioning."
332 }
333 Self::UserExperience => {
334 "Assess user experience and adoption factors. Consider ease of use, \
335 learning curve, friction points, and paths to adoption."
336 }
337 Self::RiskOpportunity => {
338 "Evaluate risks and opportunities. Consider potential downsides, \
339 upside scenarios, mitigation strategies, and contingency plans."
340 }
341 Self::Strategic => {
342 "Examine long-term strategic implications. Consider competitive advantage, \
343 strategic positioning, future optionality, and path dependencies."
344 }
345 }
346 }
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct Perspective {
356 pub id: String,
358
359 pub dimension: AnalysisDimension,
361
362 pub title: String,
364
365 pub content: String,
367
368 pub key_insight: String,
370
371 pub supporting_evidence: Vec<String>,
373
374 pub implications: Vec<String>,
376
377 pub confidence: f64,
379
380 pub novelty_score: f64,
382
383 pub depth_score: f64,
385}
386
387impl Perspective {
388 pub fn new(
390 id: impl Into<String>,
391 dimension: AnalysisDimension,
392 title: impl Into<String>,
393 content: impl Into<String>,
394 ) -> Self {
395 Self {
396 id: id.into(),
397 dimension,
398 title: title.into(),
399 content: content.into(),
400 key_insight: String::new(),
401 supporting_evidence: Vec::new(),
402 implications: Vec::new(),
403 confidence: 0.70,
404 novelty_score: 0.5,
405 depth_score: 0.5,
406 }
407 }
408
409 pub fn with_key_insight(mut self, insight: impl Into<String>) -> Self {
411 self.key_insight = insight.into();
412 self
413 }
414
415 pub fn with_evidence(mut self, evidence: impl Into<String>) -> Self {
417 self.supporting_evidence.push(evidence.into());
418 self
419 }
420
421 pub fn with_implication(mut self, implication: impl Into<String>) -> Self {
423 self.implications.push(implication.into());
424 self
425 }
426
427 pub fn with_confidence(mut self, confidence: f64) -> Self {
429 self.confidence = confidence.clamp(0.0, 1.0);
430 self
431 }
432
433 pub fn with_novelty(mut self, novelty: f64) -> Self {
435 self.novelty_score = novelty.clamp(0.0, 1.0);
436 self
437 }
438
439 pub fn with_depth(mut self, depth: f64) -> Self {
441 self.depth_score = depth.clamp(0.0, 1.0);
442 self
443 }
444
445 pub fn quality_score(&self) -> f64 {
447 (self.confidence * 0.4 + self.novelty_score * 0.3 + self.depth_score * 0.3).clamp(0.0, 1.0)
448 }
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
453pub struct Theme {
454 pub id: String,
456
457 pub title: String,
459
460 pub description: String,
462
463 pub contributing_perspectives: Vec<String>,
465
466 pub confidence: f64,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
472pub struct SynthesizedInsight {
473 pub id: String,
475
476 pub content: String,
478
479 pub source_perspectives: Vec<String>,
481
482 pub actionability: f64,
484
485 pub confidence: f64,
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct GigaThinkResult {
492 pub query: String,
494
495 pub dimensions_explored: Vec<AnalysisDimension>,
497
498 pub perspectives: Vec<Perspective>,
500
501 pub themes: Vec<Theme>,
503
504 pub insights: Vec<SynthesizedInsight>,
506
507 pub confidence: f64,
509
510 pub cross_validated: bool,
512
513 pub metadata: GigaThinkMetadata,
515}
516
517#[derive(Debug, Clone, Serialize, Deserialize)]
519pub struct GigaThinkMetadata {
520 pub version: String,
522
523 pub duration_ms: u64,
525
526 pub dimensions_count: usize,
528
529 pub perspectives_count: usize,
531
532 pub config: GigaThinkConfig,
534}
535
536pub trait AsyncThinkToolModule: ThinkToolModule {
542 fn execute_async<'a>(
544 &'a self,
545 context: &'a ThinkToolContext,
546 ) -> Pin<Box<dyn Future<Output = Result<ThinkToolOutput>> + Send + 'a>>;
547}
548
549pub struct GigaThink {
558 module_config: ThinkToolModuleConfig,
560
561 config: GigaThinkConfig,
563}
564
565impl Default for GigaThink {
566 fn default() -> Self {
567 Self::new()
568 }
569}
570
571impl GigaThink {
572 pub fn new() -> Self {
574 Self::with_config(GigaThinkConfig::default())
575 }
576
577 pub fn with_config(config: GigaThinkConfig) -> Self {
579 Self {
580 module_config: ThinkToolModuleConfig {
581 name: "GigaThink".to_string(),
582 version: "2.1.0".to_string(),
583 description: "Expansive creative thinking with 10+ diverse perspectives"
584 .to_string(),
585 confidence_weight: 0.15,
586 },
587 config,
588 }
589 }
590
591 pub fn config(&self) -> &GigaThinkConfig {
593 &self.config
594 }
595
596 fn validate_query(&self, query: &str) -> Result<()> {
598 let length = query.len();
599
600 if length < self.config.min_query_length {
601 return Err(GigaThinkError::QueryTooShort {
602 length,
603 minimum: self.config.min_query_length,
604 }
605 .into());
606 }
607
608 if length > self.config.max_query_length {
609 return Err(GigaThinkError::QueryTooLong {
610 length,
611 maximum: self.config.max_query_length,
612 }
613 .into());
614 }
615
616 Ok(())
617 }
618
619 fn get_dimensions(&self) -> Vec<AnalysisDimension> {
621 if self.config.dimensions.is_empty() {
622 AnalysisDimension::all()
623 } else {
624 self.config.dimensions.clone()
625 }
626 }
627
628 fn generate_perspectives(
630 &self,
631 query: &str,
632 dimensions: &[AnalysisDimension],
633 ) -> Vec<Perspective> {
634 dimensions
635 .iter()
636 .enumerate()
637 .map(|(idx, dim)| self.generate_perspective_for_dimension(query, *dim, idx))
638 .collect()
639 }
640
641 fn generate_perspective_for_dimension(
643 &self,
644 query: &str,
645 dimension: AnalysisDimension,
646 index: usize,
647 ) -> Perspective {
648 let id = format!("perspective_{}", index + 1);
649 let title = format!("{} Analysis", dimension.display_name());
650
651 let content = self.generate_dimension_content(query, dimension);
653 let key_insight = self.extract_key_insight(query, dimension);
654
655 let novelty_score = self.calculate_novelty_score(query, dimension);
657 let depth_score = self.calculate_depth_score(query, dimension);
658 let confidence = self.calculate_perspective_confidence(novelty_score, depth_score);
659
660 let mut perspective = Perspective::new(id, dimension, title, content)
661 .with_key_insight(key_insight)
662 .with_confidence(confidence)
663 .with_novelty(novelty_score)
664 .with_depth(depth_score);
665
666 for evidence in self.generate_evidence(query, dimension) {
668 perspective = perspective.with_evidence(evidence);
669 }
670
671 for implication in self.generate_implications(query, dimension) {
673 perspective = perspective.with_implication(implication);
674 }
675
676 perspective
677 }
678
679 fn generate_dimension_content(&self, query: &str, dimension: AnalysisDimension) -> String {
681 format!(
682 "From the {} perspective, analyzing \"{}\":\n\n{}\n\nThis dimension reveals \
683 important considerations that may not be immediately apparent in other analyses.",
684 dimension.display_name(),
685 query,
686 dimension.prompt_template()
687 )
688 }
689
690 fn extract_key_insight(&self, _query: &str, dimension: AnalysisDimension) -> String {
692 format!(
693 "The {} lens reveals unique factors that warrant deeper exploration.",
694 dimension.display_name()
695 )
696 }
697
698 fn generate_evidence(&self, _query: &str, dimension: AnalysisDimension) -> Vec<String> {
700 dimension
701 .guiding_questions()
702 .iter()
703 .map(|q| format!("Addressed: {}", q))
704 .collect()
705 }
706
707 fn generate_implications(&self, _query: &str, dimension: AnalysisDimension) -> Vec<String> {
709 vec![format!(
710 "The {} dimension has significant implications for decision-making.",
711 dimension.display_name()
712 )]
713 }
714
715 fn calculate_novelty_score(&self, _query: &str, _dimension: AnalysisDimension) -> f64 {
717 0.75
720 }
721
722 fn calculate_depth_score(&self, _query: &str, _dimension: AnalysisDimension) -> f64 {
724 0.72
727 }
728
729 fn calculate_perspective_confidence(&self, novelty: f64, depth: f64) -> f64 {
731 (novelty * self.config.novelty_weight
732 + depth * self.config.depth_weight
733 + 0.80 * self.config.coherence_weight)
734 .clamp(0.0, 1.0)
735 }
736
737 fn identify_themes(&self, perspectives: &[Perspective]) -> Vec<Theme> {
739 let mut themes = Vec::new();
743
744 if perspectives.len() >= 3 {
746 themes.push(Theme {
747 id: "theme_1".to_string(),
748 title: "Cross-Dimensional Patterns".to_string(),
749 description: "Patterns that emerge across multiple analytical dimensions."
750 .to_string(),
751 contributing_perspectives: perspectives
752 .iter()
753 .take(4)
754 .map(|p| p.id.clone())
755 .collect(),
756 confidence: 0.78,
757 });
758 }
759
760 if perspectives.len() >= 6 {
761 themes.push(Theme {
762 id: "theme_2".to_string(),
763 title: "Stakeholder Impact".to_string(),
764 description: "How different stakeholders are affected across dimensions."
765 .to_string(),
766 contributing_perspectives: perspectives
767 .iter()
768 .filter(|p| {
769 matches!(
770 p.dimension,
771 AnalysisDimension::Social
772 | AnalysisDimension::UserExperience
773 | AnalysisDimension::Ethical
774 )
775 })
776 .map(|p| p.id.clone())
777 .collect(),
778 confidence: 0.82,
779 });
780 }
781
782 themes
783 }
784
785 fn synthesize_insights(
787 &self,
788 perspectives: &[Perspective],
789 themes: &[Theme],
790 ) -> Vec<SynthesizedInsight> {
791 let mut insights = Vec::new();
792
793 let high_conf_perspectives: Vec<_> = perspectives
795 .iter()
796 .filter(|p| p.confidence > 0.75)
797 .collect();
798
799 if !high_conf_perspectives.is_empty() {
800 insights.push(SynthesizedInsight {
801 id: "insight_1".to_string(),
802 content: format!(
803 "High-confidence analysis from {} perspectives suggests actionable opportunities.",
804 high_conf_perspectives.len()
805 ),
806 source_perspectives: high_conf_perspectives.iter().map(|p| p.id.clone()).collect(),
807 actionability: 0.80,
808 confidence: 0.85,
809 });
810 }
811
812 for (idx, theme) in themes.iter().enumerate() {
814 insights.push(SynthesizedInsight {
815 id: format!("insight_{}", idx + 2),
816 content: format!(
817 "Theme '{}' integrates insights from {} perspectives.",
818 theme.title,
819 theme.contributing_perspectives.len()
820 ),
821 source_perspectives: theme.contributing_perspectives.clone(),
822 actionability: 0.70,
823 confidence: theme.confidence,
824 });
825 }
826
827 insights
828 }
829
830 fn cross_validate(&self, perspectives: &[Perspective]) -> Result<bool> {
832 if !self.config.enable_cross_validation {
833 return Ok(true);
834 }
835
836 if perspectives.len() < self.config.min_perspectives {
838 return Err(GigaThinkError::InsufficientPerspectives {
839 generated: perspectives.len(),
840 required: self.config.min_perspectives,
841 }
842 .into());
843 }
844
845 for perspective in perspectives {
847 if perspective.confidence < self.config.min_confidence {
848 return Err(GigaThinkError::LowConfidence {
849 confidence: perspective.confidence,
850 threshold: self.config.min_confidence,
851 }
852 .into());
853 }
854 }
855
856 let avg_confidence =
858 perspectives.iter().map(|p| p.confidence).sum::<f64>() / perspectives.len() as f64;
859
860 if avg_confidence < self.config.min_confidence {
861 return Err(GigaThinkError::CrossValidationFailed {
862 reason: format!(
863 "Average confidence {:.2} below threshold {:.2}",
864 avg_confidence, self.config.min_confidence
865 ),
866 }
867 .into());
868 }
869
870 Ok(true)
871 }
872
873 fn calculate_overall_confidence(&self, perspectives: &[Perspective]) -> f64 {
875 if perspectives.is_empty() {
876 return 0.0;
877 }
878
879 let total_quality: f64 = perspectives.iter().map(|p| p.quality_score()).sum();
881 let avg_quality = total_quality / perspectives.len() as f64;
882
883 let unique_dimensions: std::collections::HashSet<_> =
885 perspectives.iter().map(|p| p.dimension).collect();
886 let diversity_factor = (unique_dimensions.len() as f64 / 12.0).min(1.0);
887
888 (avg_quality * 0.7 + diversity_factor * 0.3).clamp(0.0, 1.0)
890 }
891
892 fn build_result(
894 &self,
895 context: &ThinkToolContext,
896 duration_ms: u64,
897 ) -> Result<GigaThinkResult> {
898 let query = &context.query;
899 let dimensions = self.get_dimensions();
900
901 let perspectives = self.generate_perspectives(query, &dimensions);
902 let cross_validated = self.cross_validate(&perspectives)?;
903 let themes = self.identify_themes(&perspectives);
904 let insights = self.synthesize_insights(&perspectives, &themes);
905 let confidence = self.calculate_overall_confidence(&perspectives);
906
907 Ok(GigaThinkResult {
908 query: query.clone(),
909 dimensions_explored: dimensions.clone(),
910 perspectives,
911 themes,
912 insights,
913 confidence,
914 cross_validated,
915 metadata: GigaThinkMetadata {
916 version: self.module_config.version.clone(),
917 duration_ms,
918 dimensions_count: dimensions.len(),
919 perspectives_count: self.config.min_perspectives,
920 config: self.config.clone(),
921 },
922 })
923 }
924}
925
926impl ThinkToolModule for GigaThink {
927 fn config(&self) -> &ThinkToolModuleConfig {
928 &self.module_config
929 }
930
931 fn execute(&self, context: &ThinkToolContext) -> Result<ThinkToolOutput> {
932 let start = std::time::Instant::now();
933
934 self.validate_query(&context.query)?;
936
937 let duration_ms = start.elapsed().as_millis() as u64;
939 let result = self.build_result(context, duration_ms)?;
940
941 let output = json!({
943 "dimensions": result.dimensions_explored.iter()
944 .map(|d| d.display_name())
945 .collect::<Vec<_>>(),
946 "perspectives": result.perspectives.iter().map(|p| json!({
947 "id": p.id,
948 "dimension": p.dimension.display_name(),
949 "title": p.title,
950 "key_insight": p.key_insight,
951 "confidence": p.confidence,
952 "quality_score": p.quality_score()
953 })).collect::<Vec<_>>(),
954 "themes": result.themes.iter().map(|t| json!({
955 "id": t.id,
956 "title": t.title,
957 "description": t.description,
958 "contributing_count": t.contributing_perspectives.len(),
959 "confidence": t.confidence
960 })).collect::<Vec<_>>(),
961 "insights": result.insights.iter().map(|i| json!({
962 "id": i.id,
963 "content": i.content,
964 "actionability": i.actionability,
965 "confidence": i.confidence
966 })).collect::<Vec<_>>(),
967 "confidence": result.confidence,
968 "cross_validated": result.cross_validated,
969 "metadata": {
970 "version": result.metadata.version,
971 "duration_ms": result.metadata.duration_ms,
972 "dimensions_count": result.metadata.dimensions_count,
973 "perspectives_count": result.metadata.perspectives_count
974 }
975 });
976
977 Ok(ThinkToolOutput {
978 module: self.module_config.name.clone(),
979 confidence: result.confidence,
980 output,
981 })
982 }
983}
984
985impl AsyncThinkToolModule for GigaThink {
989 fn execute_async<'a>(
990 &'a self,
991 context: &'a ThinkToolContext,
992 ) -> Pin<Box<dyn Future<Output = Result<ThinkToolOutput>> + Send + 'a>> {
993 Box::pin(async move {
994 self.execute(context)
997 })
998 }
999}
1000
1001#[derive(Default)]
1007pub struct GigaThinkBuilder {
1008 config: GigaThinkConfig,
1009}
1010
1011impl GigaThinkBuilder {
1012 pub fn new() -> Self {
1014 Self::default()
1015 }
1016
1017 pub fn min_perspectives(mut self, count: usize) -> Self {
1019 self.config.min_perspectives = count;
1020 self
1021 }
1022
1023 pub fn max_perspectives(mut self, count: usize) -> Self {
1025 self.config.max_perspectives = count;
1026 self
1027 }
1028
1029 pub fn min_confidence(mut self, confidence: f64) -> Self {
1031 self.config.min_confidence = confidence.clamp(0.0, 1.0);
1032 self
1033 }
1034
1035 pub fn cross_validation(mut self, enabled: bool) -> Self {
1037 self.config.enable_cross_validation = enabled;
1038 self
1039 }
1040
1041 pub fn dimensions(mut self, dimensions: Vec<AnalysisDimension>) -> Self {
1043 self.config.dimensions = dimensions;
1044 self
1045 }
1046
1047 pub fn novelty_weight(mut self, weight: f64) -> Self {
1049 self.config.novelty_weight = weight.clamp(0.0, 1.0);
1050 self
1051 }
1052
1053 pub fn depth_weight(mut self, weight: f64) -> Self {
1055 self.config.depth_weight = weight.clamp(0.0, 1.0);
1056 self
1057 }
1058
1059 pub fn coherence_weight(mut self, weight: f64) -> Self {
1061 self.config.coherence_weight = weight.clamp(0.0, 1.0);
1062 self
1063 }
1064
1065 pub fn max_execution_time_ms(mut self, ms: u64) -> Self {
1067 self.config.max_execution_time_ms = Some(ms);
1068 self
1069 }
1070
1071 pub fn build(self) -> GigaThink {
1073 GigaThink::with_config(self.config)
1074 }
1075}
1076
1077#[cfg(test)]
1082mod tests {
1083 use super::*;
1084
1085 #[test]
1086 fn test_gigathink_creation() {
1087 let module = GigaThink::new();
1088 use crate::thinktool::ThinkToolModule;
1090 assert_eq!(ThinkToolModule::config(&module).name, "GigaThink");
1091 assert_eq!(ThinkToolModule::config(&module).version, "2.1.0");
1092 }
1093
1094 #[test]
1095 fn test_builder_pattern() {
1096 let module = GigaThinkBuilder::new()
1097 .min_perspectives(12)
1098 .max_perspectives(20)
1099 .min_confidence(0.80)
1100 .cross_validation(false)
1101 .build();
1102
1103 assert_eq!(module.config.min_perspectives, 12);
1104 assert_eq!(module.config.max_perspectives, 20);
1105 assert_eq!(module.config.min_confidence, 0.80);
1106 assert!(!module.config.enable_cross_validation);
1107 }
1108
1109 #[test]
1110 fn test_all_dimensions() {
1111 let dimensions = AnalysisDimension::all();
1112 assert_eq!(dimensions.len(), 12);
1113 }
1114
1115 #[test]
1116 fn test_dimension_display_names() {
1117 assert_eq!(
1118 AnalysisDimension::Economic.display_name(),
1119 "Economic/Financial"
1120 );
1121 assert_eq!(
1122 AnalysisDimension::Technological.display_name(),
1123 "Technological/Innovation"
1124 );
1125 }
1126
1127 #[test]
1128 fn test_dimension_guiding_questions() {
1129 let questions = AnalysisDimension::Economic.guiding_questions();
1130 assert!(!questions.is_empty());
1131 assert!(questions[0].contains("financial"));
1132 }
1133
1134 #[test]
1135 fn test_perspective_creation() {
1136 let perspective = Perspective::new(
1137 "p1",
1138 AnalysisDimension::Economic,
1139 "Economic Analysis",
1140 "Content",
1141 )
1142 .with_key_insight("Key insight")
1143 .with_evidence("Evidence 1")
1144 .with_implication("Implication 1")
1145 .with_confidence(0.85)
1146 .with_novelty(0.75)
1147 .with_depth(0.80);
1148
1149 assert_eq!(perspective.id, "p1");
1150 assert_eq!(perspective.dimension, AnalysisDimension::Economic);
1151 assert_eq!(perspective.confidence, 0.85);
1152 assert!(!perspective.supporting_evidence.is_empty());
1153 assert!(!perspective.implications.is_empty());
1154 }
1155
1156 #[test]
1157 fn test_perspective_quality_score() {
1158 let perspective = Perspective::new("p1", AnalysisDimension::Economic, "Title", "Content")
1159 .with_confidence(0.90)
1160 .with_novelty(0.80)
1161 .with_depth(0.70);
1162
1163 let quality = perspective.quality_score();
1164 assert!((quality - 0.81).abs() < 0.01);
1166 }
1167
1168 #[test]
1169 fn test_query_validation_too_short() {
1170 let module = GigaThink::new();
1171 let result = module.validate_query("short");
1172 assert!(result.is_err());
1173 }
1174
1175 #[test]
1176 fn test_query_validation_valid() {
1177 let module = GigaThink::new();
1178 let result = module.validate_query("This is a valid query for analysis");
1179 assert!(result.is_ok());
1180 }
1181
1182 #[test]
1183 fn test_execute_generates_perspectives() {
1184 let module = GigaThink::new();
1185 let context = ThinkToolContext {
1186 query: "What are the key factors for startup success?".to_string(),
1187 previous_steps: vec![],
1188 };
1189
1190 let result = module.execute(&context).unwrap();
1191
1192 assert_eq!(result.module, "GigaThink");
1193 assert!(result.confidence > 0.0);
1194
1195 let perspectives = result
1197 .output
1198 .get("perspectives")
1199 .unwrap()
1200 .as_array()
1201 .unwrap();
1202 assert!(perspectives.len() >= 10);
1203 }
1204
1205 #[test]
1206 fn test_execute_includes_metadata() {
1207 let module = GigaThink::new();
1208 let context = ThinkToolContext {
1209 query: "What are the implications of AI on employment?".to_string(),
1210 previous_steps: vec![],
1211 };
1212
1213 let result = module.execute(&context).unwrap();
1214 let metadata = result.output.get("metadata").unwrap();
1215
1216 assert!(metadata.get("version").is_some());
1217 assert!(metadata.get("duration_ms").is_some());
1218 assert!(metadata.get("dimensions_count").is_some());
1219 }
1220
1221 #[test]
1222 fn test_cross_validation() {
1223 let module = GigaThink::new();
1224
1225 let perspectives: Vec<Perspective> = AnalysisDimension::all()
1226 .iter()
1227 .enumerate()
1228 .map(|(i, dim)| {
1229 Perspective::new(
1230 format!("p{}", i),
1231 *dim,
1232 format!("{} Analysis", dim.display_name()),
1233 "Content",
1234 )
1235 .with_confidence(0.80)
1236 })
1237 .collect();
1238
1239 let result = module.cross_validate(&perspectives);
1240 assert!(result.is_ok());
1241 }
1242
1243 #[test]
1244 fn test_cross_validation_fails_low_confidence() {
1245 let module = GigaThink::new();
1246
1247 let perspectives: Vec<Perspective> = AnalysisDimension::all()
1248 .iter()
1249 .enumerate()
1250 .map(|(i, dim)| {
1251 Perspective::new(
1252 format!("p{}", i),
1253 *dim,
1254 format!("{} Analysis", dim.display_name()),
1255 "Content",
1256 )
1257 .with_confidence(0.50) })
1259 .collect();
1260
1261 let result = module.cross_validate(&perspectives);
1262 assert!(result.is_err());
1263 }
1264
1265 #[test]
1266 fn test_theme_identification() {
1267 let module = GigaThink::new();
1268 let perspectives: Vec<Perspective> = AnalysisDimension::all()
1269 .iter()
1270 .enumerate()
1271 .map(|(i, dim)| {
1272 Perspective::new(format!("p{}", i), *dim, "Title", "Content").with_confidence(0.80)
1273 })
1274 .collect();
1275
1276 let themes = module.identify_themes(&perspectives);
1277 assert!(!themes.is_empty());
1278 }
1279
1280 #[test]
1281 fn test_insight_synthesis() {
1282 let module = GigaThink::new();
1283
1284 let perspectives: Vec<Perspective> = vec![
1285 Perspective::new("p1", AnalysisDimension::Economic, "Title", "Content")
1286 .with_confidence(0.85),
1287 Perspective::new("p2", AnalysisDimension::Social, "Title", "Content")
1288 .with_confidence(0.80),
1289 ];
1290
1291 let themes = vec![Theme {
1292 id: "t1".to_string(),
1293 title: "Theme 1".to_string(),
1294 description: "Description".to_string(),
1295 contributing_perspectives: vec!["p1".to_string(), "p2".to_string()],
1296 confidence: 0.75,
1297 }];
1298
1299 let insights = module.synthesize_insights(&perspectives, &themes);
1300 assert!(!insights.is_empty());
1301 }
1302
1303 #[tokio::test]
1304 async fn test_async_execution() {
1305 let module = GigaThink::new();
1306 let context = ThinkToolContext {
1307 query: "What are the future trends in renewable energy?".to_string(),
1308 previous_steps: vec![],
1309 };
1310
1311 let result = module.execute_async(&context).await.unwrap();
1312 assert_eq!(result.module, "GigaThink");
1313 assert!(result.confidence > 0.0);
1314 }
1315
1316 #[test]
1317 fn test_error_types() {
1318 let err = GigaThinkError::InsufficientPerspectives {
1319 generated: 5,
1320 required: 10,
1321 };
1322 assert!(err.to_string().contains("5"));
1323 assert!(err.to_string().contains("10"));
1324
1325 let err = GigaThinkError::QueryTooShort {
1326 length: 5,
1327 minimum: 10,
1328 };
1329 assert!(err.to_string().contains("too short"));
1330
1331 let err = GigaThinkError::LowConfidence {
1332 confidence: 0.50,
1333 threshold: 0.70,
1334 };
1335 assert!(err.to_string().contains("0.50"));
1336 }
1337}