1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::path::Path;
8
9use super::profiles::ReasoningProfile;
10use super::protocol::{
11 AggregationType, CritiqueSeverity, DecisionMethod, InputSpec, OutputSpec, Protocol,
12 ProtocolMetadata, ProtocolStep, ReasoningStrategy, StepAction, StepOutputFormat,
13};
14use crate::error::{Error, Result};
15
16#[derive(Debug, Clone, Deserialize, Serialize)]
18struct YamlThinkToolModule {
19 id: String,
20 name: String,
21 #[serde(default)]
22 shortcode: String,
23 category: String,
24 tier: String,
25 description: String,
26 capabilities: Vec<String>,
27 #[serde(default)]
28 output_schema: String,
29 parameters: HashMap<String, serde_json::Value>,
30 #[serde(default)]
31 confidence_factors: Vec<YamlConfidenceFactor>,
32 thinking_pattern: YamlThinkingPattern,
33 #[serde(default)]
34 typical_duration: String,
35 #[serde(default)]
36 token_cost_estimate: String,
37}
38
39#[derive(Debug, Clone, Deserialize, Serialize)]
40struct YamlConfidenceFactor {
41 factor: String,
42 weight: f64,
43 formula: String,
44}
45
46#[derive(Debug, Clone, Deserialize, Serialize)]
47struct YamlThinkingPattern {
48 #[serde(rename = "type")]
49 pattern_type: String,
50 steps: Vec<String>,
51}
52
53#[derive(Debug, Clone, Deserialize, Serialize)]
55struct YamlThinkToolsV2 {
56 version: String,
57 schema: String,
58 #[serde(default)]
59 thinktool_modules: HashMap<String, YamlThinkToolModule>,
60}
61
62pub fn load_from_yaml_file(path: &Path) -> Result<Vec<Protocol>> {
64 let content = std::fs::read_to_string(path).map_err(|e| Error::IoMessage {
65 message: format!("Failed to read YAML file {}: {}", path.display(), e),
66 })?;
67
68 load_from_yaml_string(&content)
69}
70
71pub fn load_from_yaml_string(yaml_content: &str) -> Result<Vec<Protocol>> {
73 let yaml_data: YamlThinkToolsV2 =
74 serde_yaml::from_str(yaml_content).map_err(|e| Error::Parse {
75 message: format!("Failed to parse YAML: {}", e),
76 })?;
77
78 let mut protocols = Vec::new();
79
80 for (module_key, module) in yaml_data.thinktool_modules {
81 let protocol = convert_yaml_module_to_protocol(&module_key, &module)?;
82 protocols.push(protocol);
83 }
84
85 Ok(protocols)
86}
87
88pub fn load_profiles_from_yaml_file(path: &Path) -> Result<Vec<ReasoningProfile>> {
90 let content = std::fs::read_to_string(path).map_err(|e| Error::IoMessage {
91 message: format!("Failed to read YAML file {}: {}", path.display(), e),
92 })?;
93
94 let profiles: Vec<ReasoningProfile> =
95 serde_yaml::from_str(&content).map_err(|e| Error::Parse {
96 message: format!("Failed to parse profiles YAML: {}", e),
97 })?;
98
99 Ok(profiles)
100}
101
102fn convert_yaml_module_to_protocol(
104 module_key: &str,
105 yaml_module: &YamlThinkToolModule,
106) -> Result<Protocol> {
107 let strategy = match yaml_module.category.as_str() {
109 "divergent" => ReasoningStrategy::Expansive,
110 "convergent" => ReasoningStrategy::Deductive,
111 "foundational" => ReasoningStrategy::Analytical,
112 "verification" => ReasoningStrategy::Verification,
113 "adversarial" => ReasoningStrategy::Adversarial,
114 _ => ReasoningStrategy::Analytical,
115 };
116
117 let input = build_input_spec(module_key);
119
120 let steps = build_steps_from_pattern(&yaml_module.thinking_pattern, module_key)?;
122
123 let output = build_output_spec(&yaml_module.name);
125
126 let metadata = ProtocolMetadata {
128 category: yaml_module.category.clone(),
129 composable_with: get_composable_modules(module_key),
130 typical_tokens: estimate_tokens(&yaml_module.token_cost_estimate),
131 estimated_latency_ms: estimate_latency(&yaml_module.typical_duration),
132 ..Default::default()
133 };
134
135 let protocol = Protocol {
136 id: module_key.to_string(),
137 name: yaml_module.name.clone(),
138 version: "2.0.0".to_string(),
139 description: yaml_module.description.trim().to_string(),
140 strategy,
141 input,
142 steps,
143 output,
144 validation: Vec::new(),
145 metadata,
146 };
147
148 protocol.validate().map_err(|errors| {
150 Error::Validation(format!(
151 "Invalid protocol {}: {}",
152 protocol.id,
153 errors.join(", ")
154 ))
155 })?;
156
157 Ok(protocol)
158}
159
160fn build_input_spec(module_key: &str) -> InputSpec {
162 match module_key {
163 "gigathink" => InputSpec {
164 required: vec!["query".to_string()],
165 optional: vec!["context".to_string(), "constraints".to_string()],
166 },
167 "laserlogic" => InputSpec {
168 required: vec!["argument".to_string()],
169 optional: vec!["context".to_string()],
170 },
171 "bedrock" => InputSpec {
172 required: vec!["statement".to_string()],
173 optional: vec!["domain".to_string()],
174 },
175 "proofguard" => InputSpec {
176 required: vec!["claim".to_string()],
177 optional: vec!["sources".to_string()],
178 },
179 "brutalhonesty" => InputSpec {
180 required: vec!["work".to_string()],
181 optional: vec!["criteria".to_string()],
182 },
183 _ => InputSpec::default(),
184 }
185}
186
187fn build_steps_from_pattern(
189 _pattern: &YamlThinkingPattern,
190 module_key: &str,
191) -> Result<Vec<ProtocolStep>> {
192 match module_key {
193 "gigathink" => Ok(build_gigathink_steps()),
194 "laserlogic" => Ok(build_laserlogic_steps()),
195 "bedrock" => Ok(build_bedrock_steps()),
196 "proofguard" => Ok(build_proofguard_steps()),
197 "brutalhonesty" => Ok(build_brutalhonesty_steps()),
198 "powercombo" => Ok(build_gigathink_steps()), _ => Err(Error::Validation(format!(
200 "Unknown module type: {}",
201 module_key
202 ))),
203 }
204}
205
206fn build_gigathink_steps() -> Vec<ProtocolStep> {
208 vec![
209 ProtocolStep {
210 id: "identify_dimensions".to_string(),
211 action: StepAction::Generate {
212 min_count: 5,
213 max_count: 10,
214 },
215 prompt_template:
216 r#"Identify 5-10 distinct dimensions or angles to analyze this question:
217
218Question: {{query}}
219{{#if context}}Context: {{context}}{{/if}}
220{{#if constraints}}Constraints: {{constraints}}{{/if}}
221
222For each dimension, provide a brief label. Format as a numbered list."#
223 .to_string(),
224 output_format: StepOutputFormat::List,
225 min_confidence: 0.7,
226 depends_on: vec![],
227 branch: None,
228 },
229 ProtocolStep {
230 id: "explore_perspectives".to_string(),
231 action: StepAction::Analyze {
232 criteria: vec![
233 "novelty".to_string(),
234 "relevance".to_string(),
235 "depth".to_string(),
236 ],
237 },
238 prompt_template: r#"For each dimension identified, provide:
2391. Key insight from this perspective
2402. Supporting evidence or example
2413. Implications or consequences
2424. Confidence score (0.0-1.0)
243
244Dimensions to explore:
245{{identify_dimensions}}
246
247Question: {{query}}"#
248 .to_string(),
249 output_format: StepOutputFormat::Structured,
250 min_confidence: 0.6,
251 depends_on: vec!["identify_dimensions".to_string()],
252 branch: None,
253 },
254 ProtocolStep {
255 id: "synthesize".to_string(),
256 action: StepAction::Synthesize {
257 aggregation: AggregationType::ThematicClustering,
258 },
259 prompt_template:
260 r#"Synthesize the perspectives into key themes and actionable insights:
261
262Perspectives:
263{{explore_perspectives}}
264
265Provide:
2661. Major themes (2-4)
2672. Key insights (3-5)
2683. Recommended actions (if applicable)
2694. Areas of uncertainty"#
270 .to_string(),
271 output_format: StepOutputFormat::Structured,
272 min_confidence: 0.8,
273 depends_on: vec!["explore_perspectives".to_string()],
274 branch: None,
275 },
276 ]
277}
278
279fn build_laserlogic_steps() -> Vec<ProtocolStep> {
281 vec![
282 ProtocolStep {
283 id: "extract_claims".to_string(),
284 action: StepAction::Analyze {
285 criteria: vec!["clarity".to_string(), "completeness".to_string()],
286 },
287 prompt_template: r#"Extract the logical structure from this argument:
288
289Argument: {{argument}}
290
291Identify:
2921. Main conclusion
2932. Supporting premises
2943. Implicit assumptions
2954. Causal claims (if any)
296
297Format each as a clear statement."#
298 .to_string(),
299 output_format: StepOutputFormat::Structured,
300 min_confidence: 0.7,
301 depends_on: vec![],
302 branch: None,
303 },
304 ProtocolStep {
305 id: "check_validity".to_string(),
306 action: StepAction::Validate {
307 rules: vec![
308 "logical_consistency".to_string(),
309 "premise_support".to_string(),
310 ],
311 },
312 prompt_template: r#"Evaluate the logical validity of this argument analysis:
313
314{{extract_claims}}
315
316Based on the claims identified above, check:
3171. Do the premises logically lead to the conclusion?
3182. Are there gaps in the reasoning chain?
3193. Is the argument valid (logical structure) vs sound (true premises)?
3204. Rate the logical strength (0.0-1.0) with justification"#
321 .to_string(),
322 output_format: StepOutputFormat::Structured,
323 min_confidence: 0.8,
324 depends_on: vec!["extract_claims".to_string()],
325 branch: None,
326 },
327 ProtocolStep {
328 id: "detect_fallacies".to_string(),
329 action: StepAction::Critique {
330 severity: CritiqueSeverity::Standard,
331 },
332 prompt_template: r#"Check for logical fallacies in the argument:
333
334Argument structure:
335{{extract_claims}}
336
337Common fallacies to check:
338- Ad hominem, Straw man, False dichotomy
339- Appeal to authority, Circular reasoning
340- Hasty generalization, Post hoc
341- Slippery slope, Red herring
342
343For each fallacy found, explain where and why."#
344 .to_string(),
345 output_format: StepOutputFormat::List,
346 min_confidence: 0.7,
347 depends_on: vec!["extract_claims".to_string()],
348 branch: None,
349 },
350 ]
351}
352
353fn build_bedrock_steps() -> Vec<ProtocolStep> {
355 vec![
356 ProtocolStep {
357 id: "decompose".to_string(),
358 action: StepAction::Analyze {
359 criteria: vec!["fundamentality".to_string(), "independence".to_string()],
360 },
361 prompt_template: r#"Decompose this statement to first principles:
362
363Statement: {{statement}}
364{{#if domain}}Domain: {{domain}}{{/if}}
365
366Ask repeatedly: "What is this based on? Why is this true?"
367Continue until reaching fundamental axioms or assumptions.
368
369Format as a tree structure showing dependencies."#
370 .to_string(),
371 output_format: StepOutputFormat::Structured,
372 min_confidence: 0.7,
373 depends_on: vec![],
374 branch: None,
375 },
376 ProtocolStep {
377 id: "identify_axioms".to_string(),
378 action: StepAction::Generate {
379 min_count: 3,
380 max_count: 7,
381 },
382 prompt_template: r#"From the decomposition, identify the foundational axioms:
383
384Decomposition:
385{{decompose}}
386
387For each axiom:
3881. State clearly
3892. Explain why it's fundamental (cannot be further reduced)
3903. Note if it's empirical, logical, or definitional
3914. Rate certainty (0.0-1.0)"#
392 .to_string(),
393 output_format: StepOutputFormat::List,
394 min_confidence: 0.8,
395 depends_on: vec!["decompose".to_string()],
396 branch: None,
397 },
398 ProtocolStep {
399 id: "reconstruct".to_string(),
400 action: StepAction::Synthesize {
401 aggregation: AggregationType::WeightedMerge,
402 },
403 prompt_template: r#"Reconstruct the original statement from axioms:
404
405Axioms:
406{{identify_axioms}}
407
408Original statement: {{statement}}
409
410Show the logical path from axioms to statement.
411Identify any gaps or leaps in reasoning.
412Calculate overall confidence based on axiom certainties."#
413 .to_string(),
414 output_format: StepOutputFormat::Structured,
415 min_confidence: 0.75,
416 depends_on: vec!["identify_axioms".to_string()],
417 branch: None,
418 },
419 ]
420}
421
422fn build_proofguard_steps() -> Vec<ProtocolStep> {
424 vec![
425 ProtocolStep {
426 id: "identify_sources".to_string(),
427 action: StepAction::CrossReference { min_sources: 3 },
428 prompt_template: r#"Identify potential sources to verify this claim:
429
430Claim: {{claim}}
431{{#if sources}}Known sources: {{sources}}{{/if}}
432
433List 3+ independent sources that could verify or refute this claim.
434Prioritize: official docs, peer-reviewed, primary sources."#
435 .to_string(),
436 output_format: StepOutputFormat::List,
437 min_confidence: 0.6,
438 depends_on: vec![],
439 branch: None,
440 },
441 ProtocolStep {
442 id: "verify_each".to_string(),
443 action: StepAction::Validate {
444 rules: vec![
445 "source_reliability".to_string(),
446 "claim_support".to_string(),
447 ],
448 },
449 prompt_template: r#"For each source, evaluate support for the claim:
450
451Claim: {{claim}}
452Sources to check:
453{{identify_sources}}
454
455For each source:
4561. What does it say about the claim?
4572. Support level: Confirms / Partially confirms / Neutral / Contradicts
4583. Source reliability (0.0-1.0)
4594. Key quote or evidence"#
460 .to_string(),
461 output_format: StepOutputFormat::Structured,
462 min_confidence: 0.7,
463 depends_on: vec!["identify_sources".to_string()],
464 branch: None,
465 },
466 ProtocolStep {
467 id: "triangulate".to_string(),
468 action: StepAction::Synthesize {
469 aggregation: AggregationType::Consensus,
470 },
471 prompt_template: r#"Apply triangulation to determine claim validity:
472
473Claim: {{claim}}
474Source evaluations:
475{{verify_each}}
476
477Triangulation rules:
478- 3+ independent confirming sources = HIGH confidence
479- 2 confirming, 1 neutral = MEDIUM confidence
480- Mixed results = LOW confidence, note discrepancies
481- Any contradiction = FLAG for review
482
483Provide final verdict and confidence score."#
484 .to_string(),
485 output_format: StepOutputFormat::Structured,
486 min_confidence: 0.8,
487 depends_on: vec!["verify_each".to_string()],
488 branch: None,
489 },
490 ]
491}
492
493fn build_brutalhonesty_steps() -> Vec<ProtocolStep> {
495 vec![
496 ProtocolStep {
497 id: "steelman".to_string(),
498 action: StepAction::Analyze {
499 criteria: vec!["strengths".to_string()],
500 },
501 prompt_template: r#"First, steelman the work - what are its genuine strengths?
502
503Work to critique:
504{{work}}
505
506Identify:
5071. What does this do well?
5082. What problems does it solve?
5093. What is genuinely valuable here?
510
511Be generous but honest."#
512 .to_string(),
513 output_format: StepOutputFormat::List,
514 min_confidence: 0.7,
515 depends_on: vec![],
516 branch: None,
517 },
518 ProtocolStep {
519 id: "attack".to_string(),
520 action: StepAction::Critique {
521 severity: CritiqueSeverity::Brutal,
522 },
523 prompt_template: r#"Now be brutally honest - what's wrong with this?
524
525Work:
526{{work}}
527
528Strengths identified:
529{{steelman}}
530
531Attack from all angles:
5321. Logical flaws
5332. Missing considerations
5343. Weak assumptions
5354. Implementation problems
5365. Unintended consequences
5376. What would a harsh critic say?
538
539Don't hold back. Be specific."#
540 .to_string(),
541 output_format: StepOutputFormat::List,
542 min_confidence: 0.6,
543 depends_on: vec!["steelman".to_string()],
544 branch: None,
545 },
546 ProtocolStep {
547 id: "verdict".to_string(),
548 action: StepAction::Decide {
549 method: DecisionMethod::ProsCons,
550 },
551 prompt_template: r#"Final verdict - is this work acceptable?
552
553Strengths:
554{{steelman}}
555
556Flaws:
557{{attack}}
558
559Provide:
5601. Overall assessment (Pass / Conditional Pass / Fail)
5612. Most critical issue to fix
5623. Confidence in verdict (0.0-1.0)
5634. What would make this excellent?"#
564 .to_string(),
565 output_format: StepOutputFormat::Structured,
566 min_confidence: 0.75,
567 depends_on: vec!["steelman".to_string(), "attack".to_string()],
568 branch: None,
569 },
570 ]
571}
572
573fn build_output_spec(module_name: &str) -> OutputSpec {
575 let format = format!("{}Result", module_name.replace(" ", ""));
576 let fields = match module_name {
577 "GigaThink" => vec![
578 "dimensions".to_string(),
579 "perspectives".to_string(),
580 "themes".to_string(),
581 "insights".to_string(),
582 "confidence".to_string(),
583 ],
584 "LaserLogic" => vec![
585 "conclusion".to_string(),
586 "premises".to_string(),
587 "validity".to_string(),
588 "fallacies".to_string(),
589 "confidence".to_string(),
590 ],
591 "BedRock" => vec![
592 "axioms".to_string(),
593 "decomposition".to_string(),
594 "reconstruction".to_string(),
595 "gaps".to_string(),
596 "confidence".to_string(),
597 ],
598 "ProofGuard" => vec![
599 "verdict".to_string(),
600 "sources".to_string(),
601 "evidence".to_string(),
602 "discrepancies".to_string(),
603 "confidence".to_string(),
604 ],
605 "BrutalHonesty" => vec![
606 "strengths".to_string(),
607 "flaws".to_string(),
608 "verdict".to_string(),
609 "critical_fix".to_string(),
610 "confidence".to_string(),
611 ],
612 _ => vec!["confidence".to_string()],
613 };
614
615 OutputSpec { format, fields }
616}
617
618fn get_composable_modules(module_key: &str) -> Vec<String> {
620 match module_key {
621 "gigathink" => vec!["laserlogic".to_string(), "brutalhonesty".to_string()],
622 "laserlogic" => vec!["gigathink".to_string(), "bedrock".to_string()],
623 "bedrock" => vec!["laserlogic".to_string(), "proofguard".to_string()],
624 "proofguard" => vec!["bedrock".to_string(), "brutalhonesty".to_string()],
625 "brutalhonesty" => vec!["gigathink".to_string(), "proofguard".to_string()],
626 _ => vec![],
627 }
628}
629
630fn estimate_tokens(cost_estimate: &str) -> u32 {
632 match cost_estimate {
633 "low" => 1000,
634 "medium" => 2000,
635 "medium-high" => 2500,
636 "high" => 3000,
637 _ => 2000,
638 }
639}
640
641fn estimate_latency(duration: &str) -> u32 {
643 if let Some(range) = duration.strip_suffix('s') {
645 if let Some((low, high)) = range.split_once('-') {
646 if let (Ok(low_val), Ok(high_val)) = (low.parse::<u32>(), high.parse::<u32>()) {
647 return ((low_val + high_val) / 2) * 1000; }
649 }
650 }
651 5000 }
653
654#[cfg(test)]
655mod tests {
656 use super::*;
657
658 #[test]
659 fn test_estimate_tokens() {
660 assert_eq!(estimate_tokens("low"), 1000);
661 assert_eq!(estimate_tokens("medium"), 2000);
662 assert_eq!(estimate_tokens("high"), 3000);
663 }
664
665 #[test]
666 fn test_estimate_latency() {
667 assert_eq!(estimate_latency("30-90s"), 60000);
668 assert_eq!(estimate_latency("60-180s"), 120000);
669 }
670
671 #[test]
672 fn test_build_input_spec() {
673 let spec = build_input_spec("gigathink");
674 assert_eq!(spec.required, vec!["query"]);
675 assert!(spec.optional.contains(&"context".to_string()));
676 }
677}