1use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ReasoningProfile {
17 pub id: String,
19
20 pub name: String,
22
23 pub description: String,
25
26 pub chain: Vec<ChainStep>,
28
29 pub min_confidence: f64,
31
32 pub token_budget: Option<u32>,
34
35 #[serde(default)]
37 pub tags: Vec<String>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ChainStep {
43 pub protocol_id: String,
45
46 #[serde(default)]
49 pub input_mapping: HashMap<String, String>,
50
51 #[serde(default)]
53 pub condition: Option<ChainCondition>,
54
55 #[serde(default)]
57 pub config_override: Option<StepConfigOverride>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(tag = "type", rename_all = "snake_case")]
63#[derive(Default)]
64pub enum ChainCondition {
65 #[default]
67 Always,
68
69 ConfidenceBelow {
71 threshold: f64,
73 },
74
75 ConfidenceAbove {
77 threshold: f64,
79 },
80
81 OutputExists {
83 step_id: String,
85 field: String,
87 },
88}
89
90#[derive(Debug, Clone, Default, Serialize, Deserialize)]
92pub struct StepConfigOverride {
93 pub temperature: Option<f64>,
95
96 pub max_tokens: Option<u32>,
98
99 pub min_confidence: Option<f64>,
101}
102
103use super::yaml_loader;
104
105#[derive(Debug, Default)]
107pub struct ProfileRegistry {
108 profiles: HashMap<String, ReasoningProfile>,
109}
110
111impl ProfileRegistry {
112 pub fn new() -> Self {
114 Self::default()
115 }
116
117 pub fn with_builtins() -> Self {
119 let mut registry = Self::new();
120 registry.register_builtins();
121 registry
122 }
123
124 pub fn register_builtins(&mut self) {
126 let mut loaded_from_yaml = false;
128
129 if let Ok(cwd) = std::env::current_dir() {
130 let yaml_path = cwd.join("protocols").join("profiles.yaml");
131 if yaml_path.exists() {
132 match yaml_loader::load_profiles_from_yaml_file(&yaml_path) {
133 Ok(profiles) => {
134 for profile in profiles {
135 self.register(profile);
136 }
137 tracing::info!("Loaded profiles from profiles.yaml");
138 loaded_from_yaml = true;
139 }
140 Err(e) => {
141 tracing::warn!(
142 "Failed to load profiles.yaml: {}, falling back to built-ins",
143 e
144 );
145 }
146 }
147 }
148 }
149
150 if !loaded_from_yaml {
151 tracing::info!("Using hardcoded fallback profiles");
152 self.register(builtin_quick());
153 self.register(builtin_balanced());
154 self.register(builtin_deep());
155 self.register(builtin_paranoid());
156 self.register(builtin_decide());
157 self.register(builtin_scientific());
158 self.register(builtin_powercombo());
159 }
160 }
161
162 pub fn register(&mut self, profile: ReasoningProfile) {
164 self.profiles.insert(profile.id.clone(), profile);
165 }
166
167 pub fn get(&self, id: &str) -> Option<&ReasoningProfile> {
169 self.profiles.get(id)
170 }
171
172 pub fn contains(&self, id: &str) -> bool {
174 self.profiles.contains_key(id)
175 }
176
177 pub fn list_ids(&self) -> Vec<&str> {
179 self.profiles.keys().map(|s| s.as_str()).collect()
180 }
181
182 pub fn list(&self) -> Vec<&ReasoningProfile> {
184 self.profiles.values().collect()
185 }
186
187 pub fn len(&self) -> usize {
189 self.profiles.len()
190 }
191
192 pub fn is_empty(&self) -> bool {
194 self.profiles.is_empty()
195 }
196}
197
198fn builtin_quick() -> ReasoningProfile {
205 ReasoningProfile {
206 id: "quick".to_string(),
207 name: "Quick Analysis".to_string(),
208 description: "Fast 2-step analysis for rapid insights".to_string(),
209 chain: vec![
210 ChainStep {
211 protocol_id: "gigathink".to_string(),
212 input_mapping: HashMap::from([("query".to_string(), "input.query".to_string())]),
213 condition: None,
214 config_override: Some(StepConfigOverride {
215 max_tokens: Some(1000),
216 ..Default::default()
217 }),
218 },
219 ChainStep {
220 protocol_id: "laserlogic".to_string(),
221 input_mapping: HashMap::from([
222 (
224 "argument".to_string(),
225 "steps.gigathink.synthesize".to_string(),
226 ),
227 ]),
228 condition: None,
229 config_override: None,
230 },
231 ],
232 min_confidence: 0.70,
233 token_budget: Some(3000),
234 tags: vec!["fast".to_string(), "creative".to_string()],
235 }
236}
237
238fn builtin_balanced() -> ReasoningProfile {
241 ReasoningProfile {
242 id: "balanced".to_string(),
243 name: "Balanced Analysis".to_string(),
244 description: "Standard 4-module chain for thorough but efficient analysis".to_string(),
245 chain: vec![
246 ChainStep {
247 protocol_id: "gigathink".to_string(),
248 input_mapping: HashMap::from([
249 ("query".to_string(), "input.query".to_string()),
250 ("context".to_string(), "input.context".to_string()),
251 ]),
252 condition: None,
253 config_override: None,
254 },
255 ChainStep {
256 protocol_id: "laserlogic".to_string(),
257 input_mapping: HashMap::from([(
258 "argument".to_string(),
259 "steps.gigathink.synthesize".to_string(),
260 )]),
261 condition: None,
262 config_override: None,
263 },
264 ChainStep {
265 protocol_id: "bedrock".to_string(),
266 input_mapping: HashMap::from([(
267 "statement".to_string(),
268 "steps.laserlogic.check_validity".to_string(), )]),
270 condition: Some(ChainCondition::ConfidenceBelow { threshold: 0.9 }),
271 config_override: None,
272 },
273 ChainStep {
274 protocol_id: "proofguard".to_string(),
275 input_mapping: HashMap::from([(
276 "claim".to_string(),
277 "steps.bedrock.identify_axioms".to_string(), )]),
279 condition: None,
280 config_override: None,
281 },
282 ],
283 min_confidence: 0.80,
284 token_budget: Some(8000),
285 tags: vec!["standard".to_string(), "thorough".to_string()],
286 }
287}
288
289fn builtin_deep() -> ReasoningProfile {
292 ReasoningProfile {
293 id: "deep".to_string(),
294 name: "Deep Analysis".to_string(),
295 description: "Thorough analysis with first principles and verification".to_string(),
296 chain: vec![
297 ChainStep {
298 protocol_id: "gigathink".to_string(),
299 input_mapping: HashMap::from([
300 ("query".to_string(), "input.query".to_string()),
301 ("context".to_string(), "input.context".to_string()),
302 ]),
303 condition: None,
304 config_override: None,
305 },
306 ChainStep {
307 protocol_id: "laserlogic".to_string(),
308 input_mapping: HashMap::from([(
309 "argument".to_string(),
310 "steps.gigathink.synthesize".to_string(),
311 )]),
312 condition: None,
313 config_override: None,
314 },
315 ChainStep {
316 protocol_id: "bedrock".to_string(),
317 input_mapping: HashMap::from([(
318 "statement".to_string(),
319 "steps.laserlogic.check_validity".to_string(), )]),
321 condition: None,
322 config_override: None,
323 },
324 ChainStep {
325 protocol_id: "proofguard".to_string(),
326 input_mapping: HashMap::from([(
327 "claim".to_string(),
328 "steps.bedrock.identify_axioms".to_string(), )]),
330 condition: None,
331 config_override: None,
332 },
333 ChainStep {
334 protocol_id: "brutalhonesty".to_string(),
335 input_mapping: HashMap::from([(
336 "work".to_string(),
337 "steps.proofguard.triangulate".to_string(), )]),
339 condition: Some(ChainCondition::ConfidenceBelow { threshold: 0.85 }),
340 config_override: None,
341 },
342 ],
343 min_confidence: 0.85,
344 token_budget: Some(12000),
345 tags: vec!["thorough".to_string(), "analytical".to_string()],
346 }
347}
348
349fn builtin_paranoid() -> ReasoningProfile {
352 ReasoningProfile {
353 id: "paranoid".to_string(),
354 name: "Paranoid Verification".to_string(),
355 description: "Maximum rigor with adversarial critique and multi-pass verification"
356 .to_string(),
357 chain: vec![
358 ChainStep {
359 protocol_id: "gigathink".to_string(),
360 input_mapping: HashMap::from([
361 ("query".to_string(), "input.query".to_string()),
362 ("context".to_string(), "input.context".to_string()),
363 ]),
364 condition: None,
365 config_override: None,
366 },
367 ChainStep {
368 protocol_id: "laserlogic".to_string(),
369 input_mapping: HashMap::from([(
370 "argument".to_string(),
371 "steps.gigathink.synthesize".to_string(),
372 )]),
373 condition: None,
374 config_override: None,
375 },
376 ChainStep {
377 protocol_id: "bedrock".to_string(),
378 input_mapping: HashMap::from([(
379 "statement".to_string(),
380 "steps.laserlogic.check_validity".to_string(), )]),
382 condition: None,
383 config_override: None,
384 },
385 ChainStep {
386 protocol_id: "proofguard".to_string(),
387 input_mapping: HashMap::from([(
388 "claim".to_string(),
389 "steps.bedrock.identify_axioms".to_string(), )]),
391 condition: None,
392 config_override: None,
393 },
394 ChainStep {
395 protocol_id: "brutalhonesty".to_string(),
396 input_mapping: HashMap::from([(
397 "work".to_string(),
398 "steps.proofguard.triangulate".to_string(), )]),
400 condition: None,
401 config_override: Some(StepConfigOverride {
402 temperature: Some(0.3), ..Default::default()
404 }),
405 },
406 ChainStep {
408 protocol_id: "proofguard".to_string(),
409 input_mapping: HashMap::from([(
410 "claim".to_string(),
411 "steps.brutalhonesty.verdict".to_string(), )]),
413 condition: Some(ChainCondition::ConfidenceBelow { threshold: 0.95 }),
414 config_override: None,
415 },
416 ],
417 min_confidence: 0.95,
418 token_budget: Some(18000),
419 tags: vec![
420 "rigorous".to_string(),
421 "verification".to_string(),
422 "adversarial".to_string(),
423 ],
424 }
425}
426
427fn builtin_decide() -> ReasoningProfile {
430 ReasoningProfile {
431 id: "decide".to_string(),
432 name: "Decision Support".to_string(),
433 description: "Focused on evaluating options and making decisions".to_string(),
434 chain: vec![
435 ChainStep {
436 protocol_id: "laserlogic".to_string(),
437 input_mapping: HashMap::from([("argument".to_string(), "input.query".to_string())]),
438 condition: None,
439 config_override: None,
440 },
441 ChainStep {
442 protocol_id: "bedrock".to_string(),
443 input_mapping: HashMap::from([(
444 "statement".to_string(),
445 "steps.laserlogic.check_validity".to_string(), )]),
447 condition: None,
448 config_override: None,
449 },
450 ChainStep {
451 protocol_id: "brutalhonesty".to_string(),
452 input_mapping: HashMap::from([(
453 "work".to_string(),
454 "steps.bedrock.reconstruct".to_string(), )]),
456 condition: None,
457 config_override: None,
458 },
459 ],
460 min_confidence: 0.85,
461 token_budget: Some(6000),
462 tags: vec!["decision".to_string(), "analytical".to_string()],
463 }
464}
465
466fn builtin_scientific() -> ReasoningProfile {
469 ReasoningProfile {
470 id: "scientific".to_string(),
471 name: "Scientific Method".to_string(),
472 description: "For research, hypothesis testing, and empirical analysis".to_string(),
473 chain: vec![
474 ChainStep {
475 protocol_id: "gigathink".to_string(),
476 input_mapping: HashMap::from([
477 ("query".to_string(), "input.query".to_string()),
478 ("constraints".to_string(), "input.constraints".to_string()),
479 ]),
480 condition: None,
481 config_override: Some(StepConfigOverride {
482 temperature: Some(0.8), ..Default::default()
484 }),
485 },
486 ChainStep {
487 protocol_id: "bedrock".to_string(),
488 input_mapping: HashMap::from([
489 (
490 "statement".to_string(),
491 "steps.gigathink.synthesize".to_string(), ),
493 ("domain".to_string(), "input.domain".to_string()),
494 ]),
495 condition: None,
496 config_override: None,
497 },
498 ChainStep {
499 protocol_id: "proofguard".to_string(),
500 input_mapping: HashMap::from([
501 (
502 "claim".to_string(),
503 "steps.bedrock.identify_axioms".to_string(),
504 ), ("sources".to_string(), "input.sources".to_string()),
506 ]),
507 condition: None,
508 config_override: Some(StepConfigOverride {
509 min_confidence: Some(0.85),
510 ..Default::default()
511 }),
512 },
513 ],
514 min_confidence: 0.85,
515 token_budget: Some(8000),
516 tags: vec![
517 "research".to_string(),
518 "empirical".to_string(),
519 "verification".to_string(),
520 ],
521 }
522}
523
524fn builtin_powercombo() -> ReasoningProfile {
527 ReasoningProfile {
528 id: "powercombo".to_string(),
529 name: "PowerCombo Ultimate".to_string(),
530 description: "Maximum reasoning power - all 5 ThinkTools in sequence with cross-validation"
531 .to_string(),
532 chain: vec![
533 ChainStep {
534 protocol_id: "gigathink".to_string(),
535 input_mapping: HashMap::from([
536 ("query".to_string(), "input.query".to_string()),
537 ("context".to_string(), "input.context".to_string()),
538 ]),
539 condition: None,
540 config_override: Some(StepConfigOverride {
541 temperature: Some(0.8), ..Default::default()
543 }),
544 },
545 ChainStep {
546 protocol_id: "laserlogic".to_string(),
547 input_mapping: HashMap::from([(
548 "argument".to_string(),
549 "steps.gigathink.synthesize".to_string(),
550 )]),
551 condition: None,
552 config_override: None,
553 },
554 ChainStep {
555 protocol_id: "bedrock".to_string(),
556 input_mapping: HashMap::from([(
557 "statement".to_string(),
558 "steps.laserlogic.check_validity".to_string(),
559 )]),
560 condition: None,
561 config_override: None,
562 },
563 ChainStep {
564 protocol_id: "proofguard".to_string(),
565 input_mapping: HashMap::from([(
566 "claim".to_string(),
567 "steps.bedrock.identify_axioms".to_string(),
568 )]),
569 condition: None,
570 config_override: Some(StepConfigOverride {
571 min_confidence: Some(0.9), ..Default::default()
573 }),
574 },
575 ChainStep {
576 protocol_id: "brutalhonesty".to_string(),
577 input_mapping: HashMap::from([(
578 "work".to_string(),
579 "steps.proofguard.triangulate".to_string(),
580 )]),
581 condition: None,
582 config_override: Some(StepConfigOverride {
583 temperature: Some(0.3), ..Default::default()
585 }),
586 },
587 ChainStep {
589 protocol_id: "proofguard".to_string(),
590 input_mapping: HashMap::from([(
591 "claim".to_string(),
592 "steps.brutalhonesty.verdict".to_string(),
593 )]),
594 condition: Some(ChainCondition::ConfidenceBelow { threshold: 0.95 }),
595 config_override: None,
596 },
597 ],
598 min_confidence: 0.95,
599 token_budget: Some(25000),
600 tags: vec![
601 "ultimate".to_string(),
602 "all-tools".to_string(),
603 "maximum-rigor".to_string(),
604 ],
605 }
606}
607
608#[cfg(test)]
613mod tests {
614 use super::*;
615
616 #[test]
617 fn test_profile_registry_creation() {
618 let registry = ProfileRegistry::new();
619 assert!(registry.is_empty());
620 }
621
622 #[test]
623 fn test_builtin_profiles() {
624 let registry = ProfileRegistry::with_builtins();
625
626 assert_eq!(registry.len(), 7);
627 assert!(registry.contains("quick"));
628 assert!(registry.contains("balanced"));
629 assert!(registry.contains("deep"));
630 assert!(registry.contains("paranoid"));
631 assert!(registry.contains("decide"));
632 assert!(registry.contains("scientific"));
633 assert!(registry.contains("powercombo"));
634 }
635
636 #[test]
637 fn test_get_profile() {
638 let registry = ProfileRegistry::with_builtins();
639
640 let quick = registry.get("quick").unwrap();
641 assert_eq!(quick.chain.len(), 2);
642 assert_eq!(quick.min_confidence, 0.70);
643
644 let paranoid = registry.get("paranoid").unwrap();
645 assert_eq!(paranoid.chain.len(), 6);
646 assert_eq!(paranoid.min_confidence, 0.95);
647 }
648
649 #[test]
650 fn test_profile_chain_structure() {
651 let registry = ProfileRegistry::with_builtins();
652 let balanced = registry.get("balanced").unwrap();
653
654 assert_eq!(balanced.chain[0].protocol_id, "gigathink");
656 assert_eq!(balanced.chain[1].protocol_id, "laserlogic");
657 assert_eq!(balanced.chain[2].protocol_id, "bedrock");
658 assert_eq!(balanced.chain[3].protocol_id, "proofguard");
659
660 assert!(matches!(
662 balanced.chain[2].condition,
663 Some(ChainCondition::ConfidenceBelow { threshold: 0.9 })
664 ));
665 }
666
667 #[test]
668 fn test_list_profiles() {
669 let registry = ProfileRegistry::with_builtins();
670 let ids = registry.list_ids();
671
672 assert_eq!(ids.len(), 7);
673 assert!(ids.contains(&"quick"));
674 assert!(ids.contains(&"powercombo"));
675 }
676
677 #[test]
678 fn test_powercombo_profile() {
679 let registry = ProfileRegistry::with_builtins();
680 let powercombo = registry.get("powercombo").unwrap();
681
682 assert_eq!(powercombo.chain.len(), 6);
684 assert_eq!(powercombo.min_confidence, 0.95);
685 assert_eq!(powercombo.token_budget, Some(25000));
686
687 assert_eq!(powercombo.chain[0].protocol_id, "gigathink");
689 assert_eq!(powercombo.chain[1].protocol_id, "laserlogic");
690 assert_eq!(powercombo.chain[2].protocol_id, "bedrock");
691 assert_eq!(powercombo.chain[3].protocol_id, "proofguard");
692 assert_eq!(powercombo.chain[4].protocol_id, "brutalhonesty");
693 assert_eq!(powercombo.chain[5].protocol_id, "proofguard"); }
695}