1use std::sync::{Arc, Mutex};
19use tracing::{info, warn};
20
21use crate::agent::ConversationMemory;
22use crate::config::Config;
23use crate::error::RavenClawsError;
24use crate::healing::SelfHealingEngine;
25use crate::llm::{LLMProviderTrait, MultiModelManager};
26use crate::ravenfabric::RavenFabricClient;
27
28#[derive(Debug, Clone)]
32pub struct PatternConfig {
33 pub max_rounds: usize,
35 pub max_review_iterations: usize,
37 pub research_agent_count: usize,
39 pub voter_count: usize,
41 pub verbose: bool,
43 pub tot_branches: usize,
45 pub tot_depth: usize,
47 pub tot_top_k: usize,
49 pub reflection_rounds: usize,
51}
52
53impl Default for PatternConfig {
54 fn default() -> Self {
55 Self {
56 max_rounds: 3,
57 max_review_iterations: 3,
58 research_agent_count: 3,
59 voter_count: 3,
60 verbose: false,
61 tot_branches: 3,
62 tot_depth: 3,
63 tot_top_k: 2,
64 reflection_rounds: 2,
65 }
66 }
67}
68
69pub async fn run_debate(
76 llm: Arc<dyn LLMProviderTrait>,
77 config: Config,
78 ravenfabric: Option<RavenFabricClient>,
79 pattern_config: PatternConfig,
80 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
81) -> crate::error::Result<()> {
82 info!(
83 "Starting debate mode with {} max rounds",
84 pattern_config.max_rounds
85 );
86
87 let system_prompt = &config.llm.system_prompt;
88 let task = "Analyze the given task and provide your solution.";
89
90 let positions = [
92 ("Proponent", "You argue FOR the proposition. Focus on benefits, opportunities, and strengths. Be persuasive and evidence-based."),
93 ("Opponent", "You argue AGAINST the proposition. Focus on risks, drawbacks, and weaknesses. Be critical and thorough."),
94 ("Synthesizer", "You are the neutral judge. Listen to both sides, identify common ground, and synthesize a balanced conclusion."),
95 ];
96
97 let mut debate_history = ConversationMemory::new(system_prompt, 50);
98 debate_history.add_user_message(&format!(
99 "Debate topic: {}\n\nProponent, present your opening argument.",
100 task
101 ));
102
103 for round in 0..pattern_config.max_rounds {
105 info!(round = round + 1, "Debate round starting");
106
107 for (role, persona) in &positions {
108 if *role == "Synthesizer" && round < pattern_config.max_rounds - 1 {
109 continue; }
111
112 let mut agent_memory = ConversationMemory::new(persona, 20);
113 for msg in debate_history.history() {
115 if msg.role == "system" {
116 continue;
117 }
118 if msg.role == "user" {
119 agent_memory.add_user_message(&msg.content);
120 } else {
121 agent_memory.add_assistant_message(&msg.content);
122 }
123 }
124
125 if let Some(ref healing) = healing_engine {
127 let healthy = {
128 let mut engine = healing.lock().unwrap();
129 engine.is_healthy(&format!("debate-{}", role))
130 };
131 if !healthy {
132 warn!(role = %role, round = round + 1, "Debate agent blocked by circuit breaker");
133 continue;
134 }
135 }
136
137 let messages = agent_memory.history().to_vec();
138 match llm.chat(messages).await {
139 Ok(response) => {
140 if let Some(ref healing) = healing_engine {
142 let mut engine = healing.lock().unwrap();
143 engine.record_success(&format!("debate-{}", role));
144 }
145
146 if let Some(choice) = response.choices.first() {
147 let content = &choice.message.content;
148 info!(role = %role, round = round + 1, "Debate contribution received");
149
150 if pattern_config.verbose {
151 println!("\n── {} (Round {}) ──\n{}", role, round + 1, content);
152 }
153
154 debate_history.add_assistant_message(&format!("{}: {}", role, content));
155 }
156 }
157 Err(e) => {
158 if let Some(ref healing) = healing_engine {
160 let mut engine = healing.lock().unwrap();
161 engine.record_failure(&format!("debate-{}", role), &e.to_string());
162 }
163 warn!(error = %e, role = %role, round = round + 1, "Debate LLM request failed");
164 }
165 }
166 }
167 }
168
169 let synthesizer_persona = "You are a neutral synthesizer. Produce a final balanced conclusion that incorporates the best arguments from both sides.";
171 let mut final_memory = ConversationMemory::new(synthesizer_persona, 30);
172 for msg in debate_history.history() {
173 if msg.role == "system" {
174 continue;
175 }
176 if msg.role == "user" {
177 final_memory.add_user_message(&msg.content);
178 } else {
179 final_memory.add_assistant_message(&msg.content);
180 }
181 }
182 final_memory
183 .add_user_message("Now produce your FINAL synthesis that balances all perspectives:");
184
185 if let Some(ref healing) = healing_engine {
187 let healthy = {
188 let mut engine = healing.lock().unwrap();
189 engine.is_healthy("debate-synthesis")
190 };
191 if !healthy {
192 warn!("Debate synthesis blocked by circuit breaker");
193 return Err(RavenClawsError::HealingError(
194 "Debate synthesis blocked by circuit breaker".to_string(),
195 ));
196 }
197 }
198
199 let messages = final_memory.history().to_vec();
200 match llm.chat(messages).await {
201 Ok(response) => {
202 if let Some(ref healing) = healing_engine {
204 let mut engine = healing.lock().unwrap();
205 engine.record_success("debate-synthesis");
206 }
207
208 if let Some(choice) = response.choices.first() {
209 let result = &choice.message.content;
210 println!("\n🐦⬛ Debate Synthesis:\n{}", result);
211
212 if let Some(ref rf) = ravenfabric {
213 if rf.is_enabled() {
214 let preview = result.chars().take(500).collect::<String>();
215 let _ = rf.broadcast(&preview, 30).await;
216 }
217 }
218 }
219 }
220 Err(e) => {
221 if let Some(ref healing) = healing_engine {
223 let mut engine = healing.lock().unwrap();
224 engine.record_failure("debate-synthesis", &e.to_string());
225 }
226 warn!(error = %e, "Debate synthesis failed");
227 return Err(RavenClawsError::CommandExecution(format!(
228 "Debate synthesis failed: {}",
229 e
230 )));
231 }
232 }
233
234 Ok(())
235}
236
237pub async fn run_review_loop(
245 llm: Arc<dyn LLMProviderTrait>,
246 config: Config,
247 ravenfabric: Option<RavenFabricClient>,
248 pattern_config: PatternConfig,
249 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
250) -> crate::error::Result<()> {
251 info!(
252 "Starting review-loop mode with max {} iterations",
253 pattern_config.max_review_iterations
254 );
255
256 let _system_prompt = &config.llm.system_prompt;
257 let task = "Analyze the given task and provide your solution.";
258
259 let producer_persona = "You are a producer. Create high-quality content based on the requirements. Be thorough and detailed.";
260 let reviewer_persona = "You are a reviewer. Critically evaluate the content. Identify specific issues, gaps, and improvements needed. Be constructive and precise. If the content meets quality standards, respond with APPROVED: followed by your final sign-off.";
261
262 let mut current_content = String::new();
263 let mut approved = false;
264
265 for iteration in 0..pattern_config.max_review_iterations {
266 info!(iteration = iteration + 1, "Review-loop iteration");
267
268 if iteration == 0 {
269 let mut producer_memory = ConversationMemory::new(producer_persona, 10);
271 producer_memory.add_user_message(&format!(
272 "Create content for the following task:\n\n{}",
273 task
274 ));
275
276 if let Some(ref healing) = healing_engine {
278 let healthy = {
279 let mut engine = healing.lock().unwrap();
280 engine.is_healthy("review-producer")
281 };
282 if !healthy {
283 warn!("Review producer blocked by circuit breaker");
284 return Err(RavenClawsError::HealingError(
285 "Review producer blocked by circuit breaker".to_string(),
286 ));
287 }
288 }
289
290 let messages = producer_memory.history().to_vec();
291 match llm.chat(messages).await {
292 Ok(response) => {
293 if let Some(ref healing) = healing_engine {
295 let mut engine = healing.lock().unwrap();
296 engine.record_success("review-producer");
297 }
298
299 if let Some(choice) = response.choices.first() {
300 current_content = choice.message.content.clone();
301 info!("Initial content produced: {} chars", current_content.len());
302
303 if pattern_config.verbose {
304 println!("\n── Initial Content ──\n{}", current_content);
305 }
306 }
307 }
308 Err(e) => {
309 if let Some(ref healing) = healing_engine {
311 let mut engine = healing.lock().unwrap();
312 engine.record_failure("review-producer", &e.to_string());
313 }
314 warn!(error = %e, "Producer LLM request failed");
315 return Err(RavenClawsError::CommandExecution(format!(
316 "Producer failed: {}",
317 e
318 )));
319 }
320 }
321 } else {
322 let mut reviewer_memory = ConversationMemory::new(reviewer_persona, 10);
324 reviewer_memory.add_user_message(&format!(
325 "Review the following content and provide constructive feedback:\n\n{}",
326 current_content
327 ));
328
329 if let Some(ref healing) = healing_engine {
331 let healthy = {
332 let mut engine = healing.lock().unwrap();
333 engine.is_healthy("review-reviewer")
334 };
335 if !healthy {
336 warn!("Reviewer blocked by circuit breaker");
337 continue;
338 }
339 }
340
341 let messages = reviewer_memory.history().to_vec();
342 let review = match llm.chat(messages).await {
343 Ok(response) => {
344 if let Some(ref healing) = healing_engine {
346 let mut engine = healing.lock().unwrap();
347 engine.record_success("review-reviewer");
348 }
349 response
350 .choices
351 .first()
352 .map(|c| c.message.content.clone())
353 .unwrap_or_default()
354 }
355 Err(e) => {
356 if let Some(ref healing) = healing_engine {
358 let mut engine = healing.lock().unwrap();
359 engine.record_failure("review-reviewer", &e.to_string());
360 }
361 warn!(error = %e, "Reviewer LLM request failed");
362 continue;
363 }
364 };
365
366 if pattern_config.verbose {
367 println!("\n── Review (Iteration {}) ──\n{}", iteration + 1, review);
368 }
369
370 if review.contains("APPROVED:") {
372 info!("Content approved after {} iterations", iteration + 1);
373 let final_content = review.split("APPROVED:").nth(1).unwrap_or(¤t_content);
374 current_content = final_content.trim().to_string();
375 approved = true;
376 break;
377 }
378
379 let mut producer_memory = ConversationMemory::new(producer_persona, 10);
381 producer_memory.add_user_message(&format!(
382 "Your previous content:\n\n{}\n\nReviewer feedback:\n\n{}\n\nPlease revise the content addressing all feedback.",
383 current_content, review
384 ));
385
386 if let Some(ref healing) = healing_engine {
388 let healthy = {
389 let mut engine = healing.lock().unwrap();
390 engine.is_healthy("review-producer")
391 };
392 if !healthy {
393 warn!("Producer revision blocked by circuit breaker");
394 continue;
395 }
396 }
397
398 let messages = producer_memory.history().to_vec();
399 match llm.chat(messages).await {
400 Ok(response) => {
401 if let Some(ref healing) = healing_engine {
403 let mut engine = healing.lock().unwrap();
404 engine.record_success("review-producer");
405 }
406
407 if let Some(choice) = response.choices.first() {
408 current_content = choice.message.content.clone();
409 info!("Content revised: {} chars", current_content.len());
410
411 if pattern_config.verbose {
412 println!(
413 "\n── Revised Content (Iteration {}) ──\n{}",
414 iteration + 1,
415 current_content
416 );
417 }
418 }
419 }
420 Err(e) => {
421 if let Some(ref healing) = healing_engine {
423 let mut engine = healing.lock().unwrap();
424 engine.record_failure("review-producer", &e.to_string());
425 }
426 warn!(error = %e, "Producer revision failed");
427 continue;
428 }
429 }
430 }
431 }
432
433 if !approved {
434 warn!("Review-loop reached max iterations without approval");
435 }
436
437 println!("\n🐦⬛ Review-Loop Final Content:\n{}", current_content);
438
439 if let Some(ref rf) = ravenfabric {
440 if rf.is_enabled() {
441 let preview = current_content.chars().take(500).collect::<String>();
442 let _ = rf.broadcast(&preview, 30).await;
443 }
444 }
445
446 Ok(())
447}
448
449pub async fn run_research_synthesize(
456 llm: Arc<dyn LLMProviderTrait>,
457 config: Config,
458 ravenfabric: Option<RavenFabricClient>,
459 pattern_config: PatternConfig,
460 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
461) -> crate::error::Result<()> {
462 info!(
463 "Starting research-synthesize mode with {} research agents",
464 pattern_config.research_agent_count
465 );
466
467 let _system_prompt = &config.llm.system_prompt;
468 let task = "Analyze the given task and provide your solution.";
469
470 let perspectives = [
472 ("Fact-Finder", "You are a fact-finding researcher. Focus on verifiable facts, data, statistics, and concrete evidence. Cite specific sources and numbers."),
473 ("Analyst", "You are an analytical researcher. Focus on patterns, trends, cause-and-effect relationships, and strategic implications."),
474 ("Innovator", "You are an innovative researcher. Focus on novel approaches, emerging trends, creative solutions, and future possibilities."),
475 ];
476
477 let agent_count = pattern_config.research_agent_count.min(perspectives.len());
478 let mut research_results: Vec<(String, String)> = Vec::new();
479
480 for (role, persona) in perspectives.iter().take(agent_count) {
482 info!(role = %role, "Research agent starting");
483
484 let mut memory = ConversationMemory::new(persona, 10);
485 memory.add_user_message(&format!(
486 "Research the following topic from your perspective:\n\n{}",
487 task
488 ));
489
490 if let Some(ref healing) = healing_engine {
492 let healthy = {
493 let mut engine = healing.lock().unwrap();
494 engine.is_healthy(&format!("research-{}", role))
495 };
496 if !healthy {
497 warn!(role = %role, "Research agent blocked by circuit breaker");
498 research_results.push((
499 role.to_string(),
500 "[Research blocked by circuit breaker]".to_string(),
501 ));
502 continue;
503 }
504 }
505
506 let messages = memory.history().to_vec();
507 match llm.chat(messages).await {
508 Ok(response) => {
509 if let Some(ref healing) = healing_engine {
511 let mut engine = healing.lock().unwrap();
512 engine.record_success(&format!("research-{}", role));
513 }
514
515 if let Some(choice) = response.choices.first() {
516 let content = choice.message.content.clone();
517 info!(role = %role, "Research completed: {} chars", content.len());
518 research_results.push((role.to_string(), content));
519 }
520 }
521 Err(e) => {
522 if let Some(ref healing) = healing_engine {
524 let mut engine = healing.lock().unwrap();
525 engine.record_failure(&format!("research-{}", role), &e.to_string());
526 }
527 warn!(error = %e, role = %role, "Research agent failed");
528 research_results.push((role.to_string(), format!("[Research failed: {}]", e)));
529 }
530 }
531 }
532
533 if pattern_config.verbose {
535 println!("\n── Research Findings ──");
536 for (role, content) in &research_results {
537 println!("\n--- {} ---\n{}", role, content);
538 }
539 }
540
541 let synthesizer_persona = "You are a synthesis specialist. Combine multiple research perspectives into a coherent, well-structured report. Identify common themes, resolve contradictions, and present a unified analysis.";
543 let mut synth_memory = ConversationMemory::new(synthesizer_persona, 20);
544
545 let mut synthesis_input =
546 String::from("Synthesize the following research findings into a comprehensive report:\n\n");
547 for (role, content) in &research_results {
548 synthesis_input.push_str(&format!("\n=== {} ===\n{}\n", role, content));
549 }
550 synth_memory.add_user_message(&synthesis_input);
551
552 if let Some(ref healing) = healing_engine {
554 let healthy = {
555 let mut engine = healing.lock().unwrap();
556 engine.is_healthy("research-synthesis")
557 };
558 if !healthy {
559 warn!("Research synthesis blocked by circuit breaker");
560 return Err(RavenClawsError::HealingError(
561 "Research synthesis blocked by circuit breaker".to_string(),
562 ));
563 }
564 }
565
566 let messages = synth_memory.history().to_vec();
567 match llm.chat(messages).await {
568 Ok(response) => {
569 if let Some(ref healing) = healing_engine {
571 let mut engine = healing.lock().unwrap();
572 engine.record_success("research-synthesis");
573 }
574
575 if let Some(choice) = response.choices.first() {
576 let result = &choice.message.content;
577 println!("\n🐦⬛ Research Synthesis:\n{}", result);
578
579 if let Some(ref rf) = ravenfabric {
580 if rf.is_enabled() {
581 let preview = result.chars().take(500).collect::<String>();
582 let _ = rf.broadcast(&preview, 30).await;
583 }
584 }
585 }
586 }
587 Err(e) => {
588 if let Some(ref healing) = healing_engine {
590 let mut engine = healing.lock().unwrap();
591 engine.record_failure("research-synthesis", &e.to_string());
592 }
593 warn!(error = %e, "Synthesis failed");
594 return Err(RavenClawsError::CommandExecution(format!(
595 "Synthesis failed: {}",
596 e
597 )));
598 }
599 }
600
601 Ok(())
602}
603
604pub async fn run_voting(
612 llm: Arc<dyn LLMProviderTrait>,
613 config: Config,
614 ravenfabric: Option<RavenFabricClient>,
615 pattern_config: PatternConfig,
616 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
617) -> crate::error::Result<()> {
618 info!(
619 "Starting voting mode with {} voters",
620 pattern_config.voter_count
621 );
622
623 let system_prompt = &config.llm.system_prompt;
624 let task = "Analyze the given task and provide your solution.";
625
626 let voter_personas = [
628 "You are a conservative voter. You prefer safe, proven approaches. Prioritize stability and risk mitigation. Respond with: VOTE: <your choice> REASONING: <your reasoning>",
629 "You are an aggressive voter. You prefer bold, ambitious approaches. Prioritize maximum impact and innovation. Respond with: VOTE: <your choice> REASONING: <your reasoning>",
630 "You are a balanced voter. You weigh pros and cons carefully. Prioritize pragmatic, well-rounded solutions. Respond with: VOTE: <your choice> REASONING: <your reasoning>",
631 "You are a detail-oriented voter. You focus on implementation feasibility and technical soundness. Respond with: VOTE: <your choice> REASONING: <your reasoning>",
632 "You are a user-centric voter. You prioritize user experience, accessibility, and usability. Respond with: VOTE: <your choice> REASONING: <your reasoning>",
633 ];
634
635 let voter_count = pattern_config.voter_count.min(voter_personas.len());
636 let mut votes: Vec<(String, String, String)> = Vec::new(); for (i, persona) in voter_personas.iter().enumerate().take(voter_count) {
640 let persona_name = persona
641 .split('.')
642 .next()
643 .unwrap_or(&format!("Voter {}", i + 1))
644 .to_string();
645
646 let mut memory = ConversationMemory::new(&format!("{}\n\n{}", system_prompt, persona), 10);
647 memory.add_user_message(&format!(
648 "Evaluate the following and cast your vote:\n\n{}",
649 task
650 ));
651
652 if let Some(ref healing) = healing_engine {
654 let healthy = {
655 let mut engine = healing.lock().unwrap();
656 engine.is_healthy(&format!("voter-{}", i))
657 };
658 if !healthy {
659 warn!(voter = %persona_name, "Voter blocked by circuit breaker");
660 votes.push((
661 persona_name,
662 "Error".to_string(),
663 "Vote blocked by circuit breaker".to_string(),
664 ));
665 continue;
666 }
667 }
668
669 let messages = memory.history().to_vec();
670 match llm.chat(messages).await {
671 Ok(response) => {
672 if let Some(ref healing) = healing_engine {
674 let mut engine = healing.lock().unwrap();
675 engine.record_success(&format!("voter-{}", i));
676 }
677
678 if let Some(choice) = response.choices.first() {
679 let content = choice.message.content.clone();
680 info!(voter = %persona_name, "Vote cast");
681
682 let vote = content
684 .split("VOTE:")
685 .nth(1)
686 .and_then(|s| s.split("REASONING:").next())
687 .map(|s| s.trim().to_string())
688 .unwrap_or_else(|| "Unknown".to_string());
689
690 let reasoning = content
691 .split("REASONING:")
692 .nth(1)
693 .map(|s| s.trim().to_string())
694 .unwrap_or_default();
695
696 if pattern_config.verbose {
697 println!(
698 "\n── {} ──\nVOTE: {}\nREASONING: {}",
699 persona_name, vote, reasoning
700 );
701 }
702
703 votes.push((persona_name, vote, reasoning));
704 }
705 }
706 Err(e) => {
707 if let Some(ref healing) = healing_engine {
709 let mut engine = healing.lock().unwrap();
710 engine.record_failure(&format!("voter-{}", i), &e.to_string());
711 }
712 warn!(error = %e, voter = %persona_name, "Voter LLM request failed");
713 votes.push((
714 persona_name,
715 "Error".to_string(),
716 format!("Vote failed: {}", e),
717 ));
718 }
719 }
720 }
721
722 let mut tally: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
724 for (_, vote, _) in &votes {
725 *tally.entry(vote.clone()).or_insert(0) += 1;
726 }
727
728 let max_votes = tally.values().cloned().max().unwrap_or(0);
730 let winners: Vec<String> = tally
731 .iter()
732 .filter(|(_, count)| **count == max_votes)
733 .map(|(vote, _)| vote.clone())
734 .collect();
735
736 println!("\n🐦⬛ Voting Results:");
737 println!("───");
738 for (persona, vote, reasoning) in &votes {
739 println!("{}: VOTE = {} | {}", persona, vote, reasoning);
740 }
741 println!("───");
742 println!("Tally: {:?}", tally);
743 println!("\n🏆 Decision: {}", winners.join(", "));
744
745 if let Some(ref rf) = ravenfabric {
746 if rf.is_enabled() {
747 let summary = format!(
748 "Voting completed: {} voters, decision: {}",
749 votes.len(),
750 winners.join(", ")
751 );
752 let _ = rf.broadcast(&summary, 30).await;
753 }
754 }
755
756 Ok(())
757}
758
759pub async fn run_debate_multi(
763 multi_llm: MultiModelManager,
764 config: Config,
765 _ravenfabric: Option<RavenFabricClient>,
766 pattern_config: PatternConfig,
767 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
768) -> crate::error::Result<()> {
769 info!(
770 "Starting debate mode (multi-model) with {} providers",
771 multi_llm.client_count()
772 );
773
774 let system_prompt = &config.llm.system_prompt;
775 let task = "Analyze the given task and provide your solution.";
776
777 let positions = [
778 (
779 "Proponent",
780 "You argue FOR the proposition. Focus on benefits, opportunities, and strengths.",
781 ),
782 (
783 "Opponent",
784 "You argue AGAINST the proposition. Focus on risks, drawbacks, and weaknesses.",
785 ),
786 (
787 "Synthesizer",
788 "You are the neutral judge. Synthesize a balanced conclusion.",
789 ),
790 ];
791
792 let mut debate_history = ConversationMemory::new(system_prompt, 50);
793 debate_history.add_user_message(&format!("Debate topic: {}", task));
794
795 for round in 0..pattern_config.max_rounds {
796 info!(round = round + 1, "Multi-model debate round");
797
798 for (role, persona) in &positions {
799 if *role == "Synthesizer" && round < pattern_config.max_rounds - 1 {
800 continue;
801 }
802
803 let provider_idx = round % multi_llm.client_count();
804 let client = multi_llm.get_client(provider_idx);
805
806 if let Some(client) = client {
807 let mut agent_memory = ConversationMemory::new(persona, 20);
808 for msg in debate_history.history() {
809 if msg.role == "system" {
810 continue;
811 }
812 if msg.role == "user" {
813 agent_memory.add_user_message(&msg.content);
814 } else {
815 agent_memory.add_assistant_message(&msg.content);
816 }
817 }
818
819 if let Some(ref healing) = healing_engine {
821 let healthy = {
822 let mut engine = healing.lock().unwrap();
823 engine.is_healthy(&format!("debate-multi-{}", role))
824 };
825 if !healthy {
826 warn!(role = %role, "Multi-model debate agent blocked by circuit breaker");
827 continue;
828 }
829 }
830
831 let messages = agent_memory.history().to_vec();
832 match client.chat(messages).await {
833 Ok(response) => {
834 if let Some(ref healing) = healing_engine {
836 let mut engine = healing.lock().unwrap();
837 engine.record_success(&format!("debate-multi-{}", role));
838 }
839
840 if let Some(choice) = response.choices.first() {
841 let content = &choice.message.content;
842 info!(role = %role, provider = client.provider_name(), round = round + 1, "Debate contribution");
843 if pattern_config.verbose {
844 println!(
845 "\n── {} ({} via {}, Round {}) ──\n{}",
846 role,
847 client.provider_name(),
848 client.model(),
849 round + 1,
850 content
851 );
852 }
853 debate_history.add_assistant_message(&format!(
854 "{} ({}): {}",
855 role,
856 client.provider_name(),
857 content
858 ));
859 }
860 }
861 Err(e) => {
862 if let Some(ref healing) = healing_engine {
864 let mut engine = healing.lock().unwrap();
865 engine
866 .record_failure(&format!("debate-multi-{}", role), &e.to_string());
867 }
868 warn!(error = %e, role = %role, "Multi-model debate failed");
869 }
870 }
871 }
872 }
873 }
874
875 if let Some(client) = multi_llm.get_client(0) {
877 if let Some(ref healing) = healing_engine {
879 let healthy = {
880 let mut engine = healing.lock().unwrap();
881 engine.is_healthy("debate-multi-synthesis")
882 };
883 if !healthy {
884 warn!("Multi-model debate synthesis blocked by circuit breaker");
885 return Err(RavenClawsError::HealingError(
886 "Multi-model debate synthesis blocked by circuit breaker".to_string(),
887 ));
888 }
889 }
890
891 let synthesizer_persona =
892 "You are a neutral synthesizer. Produce a final balanced conclusion.";
893 let mut final_memory = ConversationMemory::new(synthesizer_persona, 30);
894 for msg in debate_history.history() {
895 if msg.role == "system" {
896 continue;
897 }
898 if msg.role == "user" {
899 final_memory.add_user_message(&msg.content);
900 } else {
901 final_memory.add_assistant_message(&msg.content);
902 }
903 }
904 final_memory.add_user_message("Now produce your FINAL synthesis:");
905
906 let messages = final_memory.history().to_vec();
907 match client.chat(messages).await {
908 Ok(response) => {
909 if let Some(ref healing) = healing_engine {
911 let mut engine = healing.lock().unwrap();
912 engine.record_success("debate-multi-synthesis");
913 }
914
915 if let Some(choice) = response.choices.first() {
916 println!(
917 "\n🐦⬛ Multi-Model Debate Synthesis:\n{}",
918 choice.message.content
919 );
920 }
921 }
922 Err(e) => {
923 if let Some(ref healing) = healing_engine {
925 let mut engine = healing.lock().unwrap();
926 engine.record_failure("debate-multi-synthesis", &e.to_string());
927 }
928 warn!(error = %e, "Multi-model synthesis failed");
929 }
930 }
931 }
932
933 Ok(())
934}
935
936pub async fn run_review_loop_multi(
938 multi_llm: MultiModelManager,
939 _config: Config,
940 _ravenfabric: Option<RavenFabricClient>,
941 pattern_config: PatternConfig,
942 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
943) -> crate::error::Result<()> {
944 info!("Starting review-loop mode (multi-model)");
945
946 let _system_prompt = &_config.llm.system_prompt;
947 let _ = &_system_prompt;
948 let task = "Analyze the given task and provide your solution.";
949
950 let producer_persona = "You are a producer. Create high-quality content.";
951 let reviewer_persona = "You are a reviewer. Critically evaluate content. Respond with APPROVED: when quality is met.";
952
953 let mut current_content = String::new();
954 let mut approved = false;
955
956 for iteration in 0..pattern_config.max_review_iterations {
957 info!(iteration = iteration + 1, "Multi-model review-loop");
958
959 let provider_idx = iteration % multi_llm.client_count();
960 let client = multi_llm.get_client(provider_idx);
961
962 if let Some(client) = client {
963 if let Some(ref healing) = healing_engine {
965 let agent_id = if iteration == 0 {
966 "review-multi-producer"
967 } else {
968 "review-multi-reviewer"
969 };
970 let healthy = {
971 let mut engine = healing.lock().unwrap();
972 engine.is_healthy(agent_id)
973 };
974 if !healthy {
975 warn!(
976 iteration = iteration + 1,
977 "Multi-model review blocked by circuit breaker"
978 );
979 continue;
980 }
981 }
982
983 if iteration == 0 {
984 let mut memory = ConversationMemory::new(producer_persona, 10);
985 memory.add_user_message(&format!("Create content for: {}", task));
986 let messages = memory.history().to_vec();
987 match client.chat(messages).await {
988 Ok(response) => {
989 if let Some(ref healing) = healing_engine {
991 let mut engine = healing.lock().unwrap();
992 engine.record_success("review-multi-producer");
993 }
994
995 if let Some(choice) = response.choices.first() {
996 current_content = choice.message.content.clone();
997 info!(
998 "Initial content: {} chars via {}",
999 current_content.len(),
1000 client.provider_name()
1001 );
1002 }
1003 }
1004 Err(e) => {
1005 if let Some(ref healing) = healing_engine {
1007 let mut engine = healing.lock().unwrap();
1008 engine.record_failure("review-multi-producer", &e.to_string());
1009 }
1010 warn!(error = %e, "Producer failed");
1011 }
1012 }
1013 } else {
1014 let mut rev_memory = ConversationMemory::new(reviewer_persona, 10);
1016 rev_memory.add_user_message(&format!("Review:\n\n{}", current_content));
1017 let messages = rev_memory.history().to_vec();
1018 match client.chat(messages).await {
1019 Ok(response) => {
1020 if let Some(ref healing) = healing_engine {
1022 let mut engine = healing.lock().unwrap();
1023 engine.record_success("review-multi-reviewer");
1024 }
1025
1026 if let Some(choice) = response.choices.first() {
1027 let review = &choice.message.content;
1028 if review.contains("APPROVED:") {
1029 info!(
1030 "Approved after {} iterations via {}",
1031 iteration + 1,
1032 client.provider_name()
1033 );
1034 current_content = review
1035 .split("APPROVED:")
1036 .nth(1)
1037 .unwrap_or(¤t_content)
1038 .trim()
1039 .to_string();
1040 approved = true;
1041 break;
1042 }
1043 let mut prod_memory = ConversationMemory::new(producer_persona, 10);
1045 prod_memory.add_user_message(&format!(
1046 "Content:\n{}\n\nFeedback:\n{}\n\nRevise:",
1047 current_content, review
1048 ));
1049 let msgs = prod_memory.history().to_vec();
1050 if let Some(next_client) =
1051 multi_llm.get_client((iteration + 1) % multi_llm.client_count())
1052 {
1053 if let Some(ref healing) = healing_engine {
1055 let healthy = {
1056 let mut engine = healing.lock().unwrap();
1057 engine.is_healthy("review-multi-producer")
1058 };
1059 if !healthy {
1060 warn!("Multi-model producer revision blocked by circuit breaker");
1061 continue;
1062 }
1063 }
1064
1065 if let Ok(rev_resp) = next_client.chat(msgs).await {
1066 if let Some(ref healing) = healing_engine {
1068 let mut engine = healing.lock().unwrap();
1069 engine.record_success("review-multi-producer");
1070 }
1071
1072 if let Some(rev_choice) = rev_resp.choices.first() {
1073 current_content = rev_choice.message.content.clone();
1074 info!(
1075 "Revised: {} chars via {}",
1076 current_content.len(),
1077 next_client.provider_name()
1078 );
1079 }
1080 } else {
1081 if let Some(ref healing) = healing_engine {
1083 let mut engine = healing.lock().unwrap();
1084 engine.record_failure(
1085 "review-multi-producer",
1086 "revision failed",
1087 );
1088 }
1089 }
1090 }
1091 }
1092 }
1093 Err(e) => {
1094 if let Some(ref healing) = healing_engine {
1096 let mut engine = healing.lock().unwrap();
1097 engine.record_failure("review-multi-reviewer", &e.to_string());
1098 }
1099 warn!(error = %e, "Review failed");
1100 }
1101 }
1102 }
1103 }
1104 }
1105
1106 if !approved {
1107 warn!("Review-loop reached max iterations without approval");
1108 }
1109
1110 println!("\n🐦⬛ Multi-Model Review-Loop Final:\n{}", current_content);
1111 Ok(())
1112}
1113
1114pub async fn run_research_synthesize_multi(
1116 multi_llm: MultiModelManager,
1117 config: Config,
1118 _ravenfabric: Option<RavenFabricClient>,
1119 pattern_config: PatternConfig,
1120 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
1121) -> crate::error::Result<()> {
1122 info!("Starting research-synthesize mode (multi-model)");
1123
1124 let system_prompt = &config.llm.system_prompt;
1125 let task = "Analyze the given task and provide your solution.";
1126
1127 let perspectives = [
1128 (
1129 "Fact-Finder",
1130 "Focus on verifiable facts, data, statistics.",
1131 ),
1132 (
1133 "Analyst",
1134 "Focus on patterns, trends, strategic implications.",
1135 ),
1136 (
1137 "Innovator",
1138 "Focus on novel approaches and future possibilities.",
1139 ),
1140 ];
1141
1142 let agent_count = pattern_config.research_agent_count.min(perspectives.len());
1143 let mut results: Vec<(String, String, String)> = Vec::new(); for (i, (role, persona)) in perspectives.iter().enumerate().take(agent_count) {
1146 let client = multi_llm.get_client(i % multi_llm.client_count());
1147
1148 if let Some(client) = client {
1149 let mut memory =
1150 ConversationMemory::new(&format!("{}\n\n{}", system_prompt, persona), 10);
1151 memory.add_user_message(&format!("Research: {}", task));
1152 let messages = memory.history().to_vec();
1153
1154 if let Some(ref healing) = healing_engine {
1156 let healthy = {
1157 let mut engine = healing.lock().unwrap();
1158 engine.is_healthy(&format!("research-multi-{}", role))
1159 };
1160 if !healthy {
1161 warn!(role = %role, "Multi-model research agent blocked by circuit breaker");
1162 results.push((
1163 role.to_string(),
1164 "blocked".to_string(),
1165 "[Research blocked by circuit breaker]".to_string(),
1166 ));
1167 continue;
1168 }
1169 }
1170
1171 match client.chat(messages).await {
1172 Ok(response) => {
1173 if let Some(ref healing) = healing_engine {
1175 let mut engine = healing.lock().unwrap();
1176 engine.record_success(&format!("research-multi-{}", role));
1177 }
1178
1179 if let Some(choice) = response.choices.first() {
1180 let content = choice.message.content.clone();
1181 info!(role = %role, provider = client.provider_name(), "Research completed");
1182 results.push((
1183 role.to_string(),
1184 client.provider_name().to_string(),
1185 content,
1186 ));
1187 }
1188 }
1189 Err(e) => {
1190 if let Some(ref healing) = healing_engine {
1192 let mut engine = healing.lock().unwrap();
1193 engine.record_failure(&format!("research-multi-{}", role), &e.to_string());
1194 }
1195 warn!(error = %e, role = %role, "Research failed");
1196 }
1197 }
1198 }
1199 }
1200
1201 if let Some(client) = multi_llm.get_client(0) {
1203 if let Some(ref healing) = healing_engine {
1205 let healthy = {
1206 let mut engine = healing.lock().unwrap();
1207 engine.is_healthy("research-multi-synthesis")
1208 };
1209 if !healthy {
1210 warn!("Multi-model research synthesis blocked by circuit breaker");
1211 return Err(RavenClawsError::HealingError(
1212 "Multi-model research synthesis blocked by circuit breaker".to_string(),
1213 ));
1214 }
1215 }
1216
1217 let mut synth_memory = ConversationMemory::new("You are a synthesis specialist.", 20);
1218 let mut input = String::from("Synthesize these findings:\n\n");
1219 for (role, provider, content) in &results {
1220 input.push_str(&format!("=== {} ({}) ===\n{}\n", role, provider, content));
1221 }
1222 synth_memory.add_user_message(&input);
1223 let messages = synth_memory.history().to_vec();
1224
1225 match client.chat(messages).await {
1226 Ok(response) => {
1227 if let Some(ref healing) = healing_engine {
1229 let mut engine = healing.lock().unwrap();
1230 engine.record_success("research-multi-synthesis");
1231 }
1232
1233 if let Some(choice) = response.choices.first() {
1234 println!(
1235 "\n🐦⬛ Multi-Model Research Synthesis:\n{}",
1236 choice.message.content
1237 );
1238 }
1239 }
1240 Err(e) => {
1241 if let Some(ref healing) = healing_engine {
1243 let mut engine = healing.lock().unwrap();
1244 engine.record_failure("research-multi-synthesis", &e.to_string());
1245 }
1246 warn!(error = %e, "Synthesis failed");
1247 }
1248 }
1249 }
1250
1251 Ok(())
1252}
1253
1254pub async fn run_voting_multi(
1256 multi_llm: MultiModelManager,
1257 config: Config,
1258 _ravenfabric: Option<RavenFabricClient>,
1259 pattern_config: PatternConfig,
1260 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
1261) -> crate::error::Result<()> {
1262 info!(
1263 "Starting voting mode (multi-model) with {} providers",
1264 multi_llm.client_count()
1265 );
1266
1267 let system_prompt = &config.llm.system_prompt;
1268 let task = "Analyze the given task and provide your solution.";
1269
1270 let voter_personas = [
1271 "Conservative: Prefer safe, proven approaches. VOTE: <choice> REASONING: <reasoning>",
1272 "Aggressive: Prefer bold, ambitious approaches. VOTE: <choice> REASONING: <reasoning>",
1273 "Balanced: Weigh pros and cons. VOTE: <choice> REASONING: <reasoning>",
1274 "Detail-oriented: Focus on feasibility. VOTE: <choice> REASONING: <reasoning>",
1275 "User-centric: Prioritize UX. VOTE: <choice> REASONING: <reasoning>",
1276 ];
1277
1278 let voter_count = pattern_config
1279 .voter_count
1280 .min(voter_personas.len())
1281 .min(multi_llm.client_count());
1282 let mut votes: Vec<(String, String, String, String)> = Vec::new(); for (i, persona) in voter_personas.iter().enumerate().take(voter_count) {
1285 let client = multi_llm.get_client(i % multi_llm.client_count());
1286
1287 if let Some(client) = client {
1288 let mut memory =
1289 ConversationMemory::new(&format!("{}\n\n{}", system_prompt, persona), 10);
1290 memory.add_user_message(&format!("Cast your vote on: {}", task));
1291 let messages = memory.history().to_vec();
1292
1293 if let Some(ref healing) = healing_engine {
1295 let healthy = {
1296 let mut engine = healing.lock().unwrap();
1297 engine.is_healthy(&format!("voter-multi-{}", i))
1298 };
1299 if !healthy {
1300 warn!(
1301 voter = i + 1,
1302 "Multi-model voter blocked by circuit breaker"
1303 );
1304 votes.push((
1305 format!("Voter {}", i + 1),
1306 "blocked".to_string(),
1307 "Error".to_string(),
1308 "Vote blocked by circuit breaker".to_string(),
1309 ));
1310 continue;
1311 }
1312 }
1313
1314 match client.chat(messages).await {
1315 Ok(response) => {
1316 if let Some(ref healing) = healing_engine {
1318 let mut engine = healing.lock().unwrap();
1319 engine.record_success(&format!("voter-multi-{}", i));
1320 }
1321
1322 if let Some(choice) = response.choices.first() {
1323 let content = choice.message.content.clone();
1324 let vote = content
1325 .split("VOTE:")
1326 .nth(1)
1327 .and_then(|s| s.split("REASONING:").next())
1328 .map(|s| s.trim().to_string())
1329 .unwrap_or_else(|| "Unknown".to_string());
1330 let reasoning = content
1331 .split("REASONING:")
1332 .nth(1)
1333 .map(|s| s.trim().to_string())
1334 .unwrap_or_default();
1335 votes.push((
1336 format!("Voter {}", i + 1),
1337 client.provider_name().to_string(),
1338 vote,
1339 reasoning,
1340 ));
1341 }
1342 }
1343 Err(e) => {
1344 if let Some(ref healing) = healing_engine {
1346 let mut engine = healing.lock().unwrap();
1347 engine.record_failure(&format!("voter-multi-{}", i), &e.to_string());
1348 }
1349 warn!(error = %e, "Voter {} failed", i + 1);
1350 }
1351 }
1352 }
1353 }
1354
1355 let mut tally: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
1357 for (_, _, vote, _) in &votes {
1358 *tally.entry(vote.clone()).or_insert(0) += 1;
1359 }
1360
1361 println!("\n🐦⬛ Multi-Model Voting Results:");
1362 for (persona, provider, vote, reasoning) in &votes {
1363 println!(
1364 "{} ({}): VOTE = {} | {}",
1365 persona, provider, vote, reasoning
1366 );
1367 }
1368 println!("Tally: {:?}", tally);
1369 println!(
1370 "🏆 Decision: {}",
1371 tally
1372 .iter()
1373 .max_by_key(|(_, c)| *c)
1374 .map(|(v, _)| v.clone())
1375 .unwrap_or_else(|| "No decision".to_string())
1376 );
1377
1378 Ok(())
1379}
1380
1381pub async fn run_tree_of_thought(
1389 llm: Arc<dyn LLMProviderTrait>,
1390 config: Config,
1391 ravenfabric: Option<RavenFabricClient>,
1392 pattern_config: PatternConfig,
1393 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
1394) -> crate::error::Result<()> {
1395 info!(
1396 "Starting tree-of-thought mode: {} branches, depth {}, top-k {}",
1397 pattern_config.tot_branches, pattern_config.tot_depth, pattern_config.tot_top_k
1398 );
1399
1400 let system_prompt = &config.llm.system_prompt;
1401 let task = "Analyze the given task and provide your solution.";
1402
1403 let mut branches: Vec<Vec<String>> = vec![Vec::new()];
1405
1406 for depth in 0..pattern_config.tot_depth {
1407 info!(depth = depth + 1, branches = branches.len(), "ToT step");
1408
1409 if let Some(ref healing) = healing_engine {
1411 let healthy = {
1412 let mut engine = healing.lock().unwrap();
1413 engine.is_healthy("tree-of-thought")
1414 };
1415 if !healthy {
1416 warn!("Tree-of-thought blocked by circuit breaker");
1417 return Err(RavenClawsError::HealingError(
1418 "Tree-of-thought blocked by circuit breaker".to_string(),
1419 ));
1420 }
1421 }
1422
1423 let mut all_candidates: Vec<(Vec<String>, String, f64)> = Vec::new();
1424
1425 for (branch_idx, branch) in branches.iter().enumerate() {
1426 let mut memory = ConversationMemory::new(
1427 &format!("{}\n\nYou are a reasoning agent. Generate diverse candidate thoughts and evaluate them.", system_prompt),
1428 30,
1429 );
1430 memory.add_user_message(&format!(
1431 "Task: {}\n\nCurrent reasoning path (step {}/{}):\n{}\n\nGenerate {} distinct next-step thoughts. \
1432 For each thought, provide:\n1. The thought itself\n2. A confidence score (0.0 to 1.0)\n\n\
1433 Format each as:\nTHOUGHT: <your thought>\nCONFIDENCE: <0.0-1.0>",
1434 task,
1435 depth + 1,
1436 pattern_config.tot_depth,
1437 if branch.is_empty() {
1438 "No steps yet — start from scratch.".to_string()
1439 } else {
1440 branch.join("\n")
1441 },
1442 pattern_config.tot_branches,
1443 ));
1444
1445 let messages = memory.history().to_vec();
1446 match llm.chat(messages).await {
1447 Ok(response) => {
1448 if let Some(ref healing) = healing_engine {
1449 let mut engine = healing.lock().unwrap();
1450 engine.record_success("tree-of-thought");
1451 }
1452
1453 if let Some(choice) = response.choices.first() {
1454 let content = &choice.message.content;
1455 let mut current_thought = String::new();
1457 let mut current_confidence = 0.5;
1458 let mut parsing_thought = false;
1459
1460 for line in content.lines() {
1461 let trimmed = line.trim();
1462 if trimmed.starts_with("THOUGHT:") {
1463 if parsing_thought && !current_thought.is_empty() {
1465 let mut new_branch = branch.clone();
1466 new_branch.push(current_thought.clone());
1467 all_candidates.push((
1468 new_branch,
1469 format!("Confidence: {:.2}", current_confidence),
1470 current_confidence,
1471 ));
1472 }
1473 current_thought = trimmed
1474 .strip_prefix("THOUGHT:")
1475 .unwrap_or("")
1476 .trim()
1477 .to_string();
1478 parsing_thought = true;
1479 current_confidence = 0.5;
1480 } else if trimmed.starts_with("CONFIDENCE:") {
1481 let val_str =
1482 trimmed.strip_prefix("CONFIDENCE:").unwrap_or("0.5").trim();
1483 if let Ok(val) = val_str.parse::<f64>() {
1484 current_confidence = val.clamp(0.0, 1.0);
1485 }
1486 } else if parsing_thought && !trimmed.is_empty() {
1487 current_thought.push(' ');
1488 current_thought.push_str(trimmed);
1489 }
1490 }
1491 if parsing_thought && !current_thought.is_empty() {
1493 let mut new_branch = branch.clone();
1494 new_branch.push(current_thought);
1495 all_candidates.push((
1496 new_branch,
1497 format!("Confidence: {:.2}", current_confidence),
1498 current_confidence,
1499 ));
1500 }
1501 }
1502 }
1503 Err(e) => {
1504 if let Some(ref healing) = healing_engine {
1505 let mut engine = healing.lock().unwrap();
1506 engine.record_failure("tree-of-thought", &e.to_string());
1507 }
1508 warn!(error = %e, "Tree-of-thought branch {} failed", branch_idx);
1509 }
1510 }
1511 }
1512
1513 if all_candidates.is_empty() {
1514 warn!("No candidates generated at depth {}", depth + 1);
1515 break;
1516 }
1517
1518 all_candidates.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
1520 let top_k = pattern_config.tot_top_k.min(all_candidates.len());
1521 branches = all_candidates
1522 .into_iter()
1523 .take(top_k)
1524 .map(|(branch, _, _)| branch)
1525 .collect();
1526
1527 if pattern_config.verbose {
1528 println!(
1529 "\n── ToT Depth {}/{} ──",
1530 depth + 1,
1531 pattern_config.tot_depth
1532 );
1533 for (i, branch) in branches.iter().enumerate() {
1534 println!("Branch {} ({} steps):", i + 1, branch.len());
1535 for (j, thought) in branch.iter().enumerate() {
1536 println!(" Step {}: {}", j + 1, thought);
1537 }
1538 println!();
1539 }
1540 }
1541 }
1542
1543 let best_branch = branches.into_iter().next().unwrap_or_default();
1545 let mut final_memory = ConversationMemory::new(
1546 &format!(
1547 "{}\n\nYou are a synthesizer. Produce a final answer based on the best reasoning path.",
1548 system_prompt
1549 ),
1550 20,
1551 );
1552 final_memory.add_user_message(&format!(
1553 "Task: {}\n\nBest reasoning path:\n{}\n\nProduce a final, well-reasoned answer:",
1554 task,
1555 best_branch.join("\n→ "),
1556 ));
1557
1558 let messages = final_memory.history().to_vec();
1559 match llm.chat(messages).await {
1560 Ok(response) => {
1561 if let Some(ref healing) = healing_engine {
1562 let mut engine = healing.lock().unwrap();
1563 engine.record_success("tree-of-thought-synthesis");
1564 }
1565
1566 if let Some(choice) = response.choices.first() {
1567 println!(
1568 "\n🐦⬛ Tree-of-Thought Final Answer:\n{}",
1569 choice.message.content
1570 );
1571
1572 if let Some(ref rf) = ravenfabric {
1573 if rf.is_enabled() {
1574 let summary = format!(
1575 "Tree-of-Thought completed: depth {}, branches explored",
1576 pattern_config.tot_depth
1577 );
1578 let _ = rf.broadcast(&summary, 30).await;
1579 }
1580 }
1581 }
1582 }
1583 Err(e) => {
1584 if let Some(ref healing) = healing_engine {
1585 let mut engine = healing.lock().unwrap();
1586 engine.record_failure("tree-of-thought-synthesis", &e.to_string());
1587 }
1588 warn!(error = %e, "Tree-of-thought synthesis failed");
1589 }
1590 }
1591
1592 Ok(())
1593}
1594
1595pub async fn run_tree_of_thought_multi(
1597 multi_llm: MultiModelManager,
1598 config: Config,
1599 ravenfabric: Option<RavenFabricClient>,
1600 pattern_config: PatternConfig,
1601 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
1602) -> crate::error::Result<()> {
1603 info!(
1604 "Starting tree-of-thought mode (multi-model): {} branches, depth {}, top-k {}",
1605 pattern_config.tot_branches, pattern_config.tot_depth, pattern_config.tot_top_k
1606 );
1607
1608 let system_prompt = &config.llm.system_prompt;
1609 let task = "Analyze the given task and provide your solution.";
1610
1611 let mut branches: Vec<Vec<String>> = vec![Vec::new()];
1612
1613 for depth in 0..pattern_config.tot_depth {
1614 info!(
1615 depth = depth + 1,
1616 branches = branches.len(),
1617 "ToT multi step"
1618 );
1619
1620 if let Some(ref healing) = healing_engine {
1622 let healthy = {
1623 let mut engine = healing.lock().unwrap();
1624 engine.is_healthy("tree-of-thought-multi")
1625 };
1626 if !healthy {
1627 warn!("Tree-of-thought (multi) blocked by circuit breaker");
1628 return Err(RavenClawsError::HealingError(
1629 "Tree-of-thought (multi) blocked by circuit breaker".to_string(),
1630 ));
1631 }
1632 }
1633
1634 let mut all_candidates: Vec<(Vec<String>, String, f64)> = Vec::new();
1635
1636 for (branch_idx, branch) in branches.iter().enumerate() {
1637 let provider_idx = (depth * branches.len() + branch_idx) % multi_llm.client_count();
1638 let client = multi_llm.get_client(provider_idx);
1639
1640 if let Some(client) = client {
1641 let mut memory = ConversationMemory::new(
1642 &format!("{}\n\nYou are a reasoning agent. Generate diverse candidate thoughts and evaluate them.", system_prompt),
1643 30,
1644 );
1645 memory.add_user_message(&format!(
1646 "Task: {}\n\nCurrent reasoning path (step {}/{}):\n{}\n\nGenerate {} distinct next-step thoughts. \
1647 For each thought, provide:\n1. The thought itself\n2. A confidence score (0.0 to 1.0)\n\n\
1648 Format each as:\nTHOUGHT: <your thought>\nCONFIDENCE: <0.0-1.0>",
1649 task,
1650 depth + 1,
1651 pattern_config.tot_depth,
1652 if branch.is_empty() {
1653 "No steps yet — start from scratch.".to_string()
1654 } else {
1655 branch.join("\n")
1656 },
1657 pattern_config.tot_branches,
1658 ));
1659
1660 let messages = memory.history().to_vec();
1661 match client.chat(messages).await {
1662 Ok(response) => {
1663 if let Some(ref healing) = healing_engine {
1664 let mut engine = healing.lock().unwrap();
1665 engine.record_success("tree-of-thought-multi");
1666 }
1667
1668 if let Some(choice) = response.choices.first() {
1669 let content = &choice.message.content;
1670 let mut current_thought = String::new();
1671 let mut current_confidence = 0.5;
1672 let mut parsing_thought = false;
1673
1674 for line in content.lines() {
1675 let trimmed = line.trim();
1676 if trimmed.starts_with("THOUGHT:") {
1677 if parsing_thought && !current_thought.is_empty() {
1678 let mut new_branch = branch.clone();
1679 new_branch.push(current_thought.clone());
1680 all_candidates.push((
1681 new_branch,
1682 format!("Confidence: {:.2}", current_confidence),
1683 current_confidence,
1684 ));
1685 }
1686 current_thought = trimmed
1687 .strip_prefix("THOUGHT:")
1688 .unwrap_or("")
1689 .trim()
1690 .to_string();
1691 parsing_thought = true;
1692 current_confidence = 0.5;
1693 } else if trimmed.starts_with("CONFIDENCE:") {
1694 let val_str =
1695 trimmed.strip_prefix("CONFIDENCE:").unwrap_or("0.5").trim();
1696 if let Ok(val) = val_str.parse::<f64>() {
1697 current_confidence = val.clamp(0.0, 1.0);
1698 }
1699 } else if parsing_thought && !trimmed.is_empty() {
1700 current_thought.push(' ');
1701 current_thought.push_str(trimmed);
1702 }
1703 }
1704 if parsing_thought && !current_thought.is_empty() {
1705 let mut new_branch = branch.clone();
1706 new_branch.push(current_thought);
1707 all_candidates.push((
1708 new_branch,
1709 format!("Confidence: {:.2}", current_confidence),
1710 current_confidence,
1711 ));
1712 }
1713 }
1714 }
1715 Err(e) => {
1716 if let Some(ref healing) = healing_engine {
1717 let mut engine = healing.lock().unwrap();
1718 engine.record_failure("tree-of-thought-multi", &e.to_string());
1719 }
1720 warn!(error = %e, "ToT multi branch {} failed", branch_idx);
1721 }
1722 }
1723 }
1724 }
1725
1726 if all_candidates.is_empty() {
1727 warn!("No candidates generated at depth {}", depth + 1);
1728 break;
1729 }
1730
1731 all_candidates.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(std::cmp::Ordering::Equal));
1732 let top_k = pattern_config.tot_top_k.min(all_candidates.len());
1733 branches = all_candidates
1734 .into_iter()
1735 .take(top_k)
1736 .map(|(branch, _, _)| branch)
1737 .collect();
1738
1739 if pattern_config.verbose {
1740 println!(
1741 "\n── ToT Multi Depth {}/{} ──",
1742 depth + 1,
1743 pattern_config.tot_depth
1744 );
1745 for (i, branch) in branches.iter().enumerate() {
1746 println!("Branch {} ({} steps):", i + 1, branch.len());
1747 for (j, thought) in branch.iter().enumerate() {
1748 println!(" Step {}: {}", j + 1, thought);
1749 }
1750 println!();
1751 }
1752 }
1753 }
1754
1755 let best_branch = branches.into_iter().next().unwrap_or_default();
1757 if let Some(client) = multi_llm.get_client(0) {
1758 if let Some(ref healing) = healing_engine {
1759 let healthy = {
1760 let mut engine = healing.lock().unwrap();
1761 engine.is_healthy("tree-of-thought-multi-synthesis")
1762 };
1763 if !healthy {
1764 warn!("ToT multi synthesis blocked by circuit breaker");
1765 return Err(RavenClawsError::HealingError(
1766 "ToT multi synthesis blocked by circuit breaker".to_string(),
1767 ));
1768 }
1769 }
1770
1771 let mut final_memory = ConversationMemory::new(
1772 &format!("{}\n\nYou are a synthesizer. Produce a final answer based on the best reasoning path.", system_prompt),
1773 20,
1774 );
1775 final_memory.add_user_message(&format!(
1776 "Task: {}\n\nBest reasoning path:\n{}\n\nProduce a final, well-reasoned answer:",
1777 task,
1778 best_branch.join("\n→ "),
1779 ));
1780
1781 let messages = final_memory.history().to_vec();
1782 match client.chat(messages).await {
1783 Ok(response) => {
1784 if let Some(ref healing) = healing_engine {
1785 let mut engine = healing.lock().unwrap();
1786 engine.record_success("tree-of-thought-multi-synthesis");
1787 }
1788
1789 if let Some(choice) = response.choices.first() {
1790 println!(
1791 "\n🐦⬛ Tree-of-Thought (Multi) Final Answer:\n{}",
1792 choice.message.content
1793 );
1794
1795 if let Some(ref rf) = ravenfabric {
1796 if rf.is_enabled() {
1797 let summary = format!(
1798 "Tree-of-Thought (multi) completed: depth {}, branches explored",
1799 pattern_config.tot_depth
1800 );
1801 let _ = rf.broadcast(&summary, 30).await;
1802 }
1803 }
1804 }
1805 }
1806 Err(e) => {
1807 if let Some(ref healing) = healing_engine {
1808 let mut engine = healing.lock().unwrap();
1809 engine.record_failure("tree-of-thought-multi-synthesis", &e.to_string());
1810 }
1811 warn!(error = %e, "ToT multi synthesis failed");
1812 }
1813 }
1814 }
1815
1816 Ok(())
1817}
1818
1819pub async fn run_self_reflection(
1827 llm: Arc<dyn LLMProviderTrait>,
1828 config: Config,
1829 ravenfabric: Option<RavenFabricClient>,
1830 pattern_config: PatternConfig,
1831 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
1832) -> crate::error::Result<()> {
1833 info!(
1834 "Starting self-reflection mode with {} reflection rounds",
1835 pattern_config.reflection_rounds
1836 );
1837
1838 let system_prompt = &config.llm.system_prompt;
1839 let task = "Analyze the given task and provide your solution.";
1840
1841 let mut current_solution = String::new();
1843
1844 {
1845 let mut memory = ConversationMemory::new(
1846 &format!(
1847 "{}\n\nYou are a problem solver. Generate a thorough solution.",
1848 system_prompt
1849 ),
1850 10,
1851 );
1852 memory.add_user_message(&format!("Task: {}\n\nProvide your solution:", task));
1853 let messages = memory.history().to_vec();
1854
1855 if let Some(ref healing) = healing_engine {
1857 let healthy = {
1858 let mut engine = healing.lock().unwrap();
1859 engine.is_healthy("self-reflection-generate")
1860 };
1861 if !healthy {
1862 warn!("Self-reflection generation blocked by circuit breaker");
1863 return Err(RavenClawsError::HealingError(
1864 "Self-reflection generation blocked by circuit breaker".to_string(),
1865 ));
1866 }
1867 }
1868
1869 match llm.chat(messages).await {
1870 Ok(response) => {
1871 if let Some(ref healing) = healing_engine {
1872 let mut engine = healing.lock().unwrap();
1873 engine.record_success("self-reflection-generate");
1874 }
1875
1876 if let Some(choice) = response.choices.first() {
1877 current_solution = choice.message.content.clone();
1878 info!("Initial solution: {} chars", current_solution.len());
1879 if pattern_config.verbose {
1880 println!(
1881 "\n── Self-Reflection: Initial Solution ──\n{}",
1882 current_solution
1883 );
1884 }
1885 }
1886 }
1887 Err(e) => {
1888 if let Some(ref healing) = healing_engine {
1889 let mut engine = healing.lock().unwrap();
1890 engine.record_failure("self-reflection-generate", &e.to_string());
1891 }
1892 warn!(error = %e, "Initial solution generation failed");
1893 return Err(RavenClawsError::Llm(crate::llm::LLMError::RequestFailed(
1894 format!("Initial solution generation failed: {}", e),
1895 )));
1896 }
1897 }
1898 }
1899
1900 for round in 0..pattern_config.reflection_rounds {
1902 info!(round = round + 1, "Self-reflection round");
1903
1904 if let Some(ref healing) = healing_engine {
1906 let healthy = {
1907 let mut engine = healing.lock().unwrap();
1908 engine.is_healthy(&format!("self-reflection-round-{}", round))
1909 };
1910 if !healthy {
1911 warn!(
1912 round = round + 1,
1913 "Self-reflection round blocked by circuit breaker"
1914 );
1915 break;
1916 }
1917 }
1918
1919 let mut reflection = String::new();
1921 {
1922 let mut reflect_memory = ConversationMemory::new(
1923 "You are a critical reviewer. Analyze solutions for gaps, errors, logical flaws, \
1924 missing edge cases, and opportunities for improvement. Be thorough and constructive.",
1925 10,
1926 );
1927 reflect_memory.add_user_message(&format!(
1928 "Review this solution critically:\n\n{}\n\nIdentify:\n1. Gaps or missing elements\n\
1929 2. Logical flaws or errors\n3. Edge cases not handled\n4. Opportunities for improvement\n\
1930 5. Overall quality assessment",
1931 current_solution
1932 ));
1933 let messages = reflect_memory.history().to_vec();
1934
1935 match llm.chat(messages).await {
1936 Ok(response) => {
1937 if let Some(ref healing) = healing_engine {
1938 let mut engine = healing.lock().unwrap();
1939 engine.record_success(&format!("self-reflection-round-{}", round));
1940 }
1941
1942 if let Some(choice) = response.choices.first() {
1943 reflection = choice.message.content.clone();
1944 if pattern_config.verbose {
1945 println!(
1946 "\n── Self-Reflection Round {}/{}: Reflection ──\n{}",
1947 round + 1,
1948 pattern_config.reflection_rounds,
1949 reflection
1950 );
1951 }
1952 }
1953 }
1954 Err(e) => {
1955 if let Some(ref healing) = healing_engine {
1956 let mut engine = healing.lock().unwrap();
1957 engine.record_failure(
1958 &format!("self-reflection-round-{}", round),
1959 &e.to_string(),
1960 );
1961 }
1962 warn!(error = %e, round = round + 1, "Reflection failed");
1963 continue;
1964 }
1965 }
1966 }
1967
1968 if !reflection.is_empty() {
1970 let mut improve_memory = ConversationMemory::new(
1971 &format!(
1972 "{}\n\nYou are an improver. Take feedback and produce a better version.",
1973 system_prompt
1974 ),
1975 10,
1976 );
1977 improve_memory.add_user_message(&format!(
1978 "Original solution:\n{}\n\nFeedback received:\n{}\n\n\
1979 Produce an improved solution that addresses all feedback. \
1980 Mark your response with IMPROVED: at the start.",
1981 current_solution, reflection
1982 ));
1983 let messages = improve_memory.history().to_vec();
1984
1985 match llm.chat(messages).await {
1986 Ok(response) => {
1987 if let Some(choice) = response.choices.first() {
1988 let improved = choice.message.content.clone();
1989 let improved_clean = improved
1990 .strip_prefix("IMPROVED:")
1991 .unwrap_or(&improved)
1992 .trim()
1993 .to_string();
1994 current_solution = improved_clean;
1995 info!(
1996 round = round + 1,
1997 "Solution improved: {} chars",
1998 current_solution.len()
1999 );
2000 }
2001 }
2002 Err(e) => {
2003 warn!(error = %e, round = round + 1, "Improvement failed");
2004 }
2005 }
2006 }
2007 }
2008
2009 println!(
2010 "\n🐦⬛ Self-Reflection Final Solution (after {} rounds):\n{}",
2011 pattern_config.reflection_rounds, current_solution
2012 );
2013
2014 if let Some(ref rf) = ravenfabric {
2015 if rf.is_enabled() {
2016 let summary = format!(
2017 "Self-reflection completed: {} rounds, final solution {} chars",
2018 pattern_config.reflection_rounds,
2019 current_solution.len()
2020 );
2021 let _ = rf.broadcast(&summary, 30).await;
2022 }
2023 }
2024
2025 Ok(())
2026}
2027
2028pub async fn run_self_reflection_multi(
2030 multi_llm: MultiModelManager,
2031 config: Config,
2032 ravenfabric: Option<RavenFabricClient>,
2033 pattern_config: PatternConfig,
2034 healing_engine: Option<Arc<Mutex<SelfHealingEngine>>>,
2035) -> crate::error::Result<()> {
2036 info!(
2037 "Starting self-reflection mode (multi-model) with {} rounds",
2038 pattern_config.reflection_rounds
2039 );
2040
2041 let system_prompt = &config.llm.system_prompt;
2042 let task = "Analyze the given task and provide your solution.";
2043
2044 let mut current_solution = String::new();
2046
2047 if let Some(client) = multi_llm.get_client(0) {
2048 if let Some(ref healing) = healing_engine {
2049 let healthy = {
2050 let mut engine = healing.lock().unwrap();
2051 engine.is_healthy("self-reflection-multi-generate")
2052 };
2053 if !healthy {
2054 warn!("Self-reflection multi generation blocked by circuit breaker");
2055 return Err(RavenClawsError::HealingError(
2056 "Self-reflection multi generation blocked by circuit breaker".to_string(),
2057 ));
2058 }
2059 }
2060
2061 let mut memory = ConversationMemory::new(
2062 &format!(
2063 "{}\n\nYou are a problem solver. Generate a thorough solution.",
2064 system_prompt
2065 ),
2066 10,
2067 );
2068 memory.add_user_message(&format!("Task: {}\n\nProvide your solution:", task));
2069 let messages = memory.history().to_vec();
2070
2071 match client.chat(messages).await {
2072 Ok(response) => {
2073 if let Some(ref healing) = healing_engine {
2074 let mut engine = healing.lock().unwrap();
2075 engine.record_success("self-reflection-multi-generate");
2076 }
2077
2078 if let Some(choice) = response.choices.first() {
2079 current_solution = choice.message.content.clone();
2080 info!(
2081 "Initial solution: {} chars via {}",
2082 current_solution.len(),
2083 client.provider_name()
2084 );
2085 if pattern_config.verbose {
2086 println!(
2087 "\n── Self-Reflection Multi: Initial Solution ──\n{}",
2088 current_solution
2089 );
2090 }
2091 }
2092 }
2093 Err(e) => {
2094 if let Some(ref healing) = healing_engine {
2095 let mut engine = healing.lock().unwrap();
2096 engine.record_failure("self-reflection-multi-generate", &e.to_string());
2097 }
2098 warn!(error = %e, "Multi initial solution generation failed");
2099 return Err(RavenClawsError::Llm(crate::llm::LLMError::RequestFailed(
2100 format!("Multi initial solution generation failed: {}", e),
2101 )));
2102 }
2103 }
2104 }
2105
2106 for round in 0..pattern_config.reflection_rounds {
2108 info!(round = round + 1, "Self-reflection multi round");
2109
2110 let reflector_idx = (round * 2 + 1) % multi_llm.client_count();
2112 let improver_idx = (round * 2 + 2) % multi_llm.client_count();
2113
2114 if let Some(ref healing) = healing_engine {
2116 let healthy = {
2117 let mut engine = healing.lock().unwrap();
2118 engine.is_healthy(&format!("self-reflection-multi-round-{}", round))
2119 };
2120 if !healthy {
2121 warn!(
2122 round = round + 1,
2123 "Self-reflection multi round blocked by circuit breaker"
2124 );
2125 break;
2126 }
2127 }
2128
2129 let mut reflection = String::new();
2131 if let Some(reflector) = multi_llm.get_client(reflector_idx) {
2132 let mut reflect_memory = ConversationMemory::new(
2133 "You are a critical reviewer. Analyze solutions for gaps, errors, logical flaws, \
2134 missing edge cases, and opportunities for improvement. Be thorough and constructive.",
2135 10,
2136 );
2137 reflect_memory.add_user_message(&format!(
2138 "Review this solution critically:\n\n{}\n\nIdentify:\n1. Gaps or missing elements\n\
2139 2. Logical flaws or errors\n3. Edge cases not handled\n4. Opportunities for improvement\n\
2140 5. Overall quality assessment",
2141 current_solution
2142 ));
2143 let messages = reflect_memory.history().to_vec();
2144
2145 match reflector.chat(messages).await {
2146 Ok(response) => {
2147 if let Some(ref healing) = healing_engine {
2148 let mut engine = healing.lock().unwrap();
2149 engine.record_success(&format!("self-reflection-multi-round-{}", round));
2150 }
2151
2152 if let Some(choice) = response.choices.first() {
2153 reflection = choice.message.content.clone();
2154 if pattern_config.verbose {
2155 println!(
2156 "\n── Self-Reflection Multi Round {}/{}: Reflection ({} via {}) ──\n{}",
2157 round + 1,
2158 pattern_config.reflection_rounds,
2159 reflector.provider_name(),
2160 reflector.model(),
2161 reflection
2162 );
2163 }
2164 }
2165 }
2166 Err(e) => {
2167 if let Some(ref healing) = healing_engine {
2168 let mut engine = healing.lock().unwrap();
2169 engine.record_failure(
2170 &format!("self-reflection-multi-round-{}", round),
2171 &e.to_string(),
2172 );
2173 }
2174 warn!(error = %e, round = round + 1, "Multi reflection failed");
2175 continue;
2176 }
2177 }
2178 }
2179
2180 if !reflection.is_empty() {
2182 if let Some(improver) = multi_llm.get_client(improver_idx) {
2183 let mut improve_memory = ConversationMemory::new(
2184 &format!(
2185 "{}\n\nYou are an improver. Take feedback and produce a better version.",
2186 system_prompt
2187 ),
2188 10,
2189 );
2190 improve_memory.add_user_message(&format!(
2191 "Original solution:\n{}\n\nFeedback received:\n{}\n\n\
2192 Produce an improved solution that addresses all feedback. \
2193 Mark your response with IMPROVED: at the start.",
2194 current_solution, reflection
2195 ));
2196 let messages = improve_memory.history().to_vec();
2197
2198 match improver.chat(messages).await {
2199 Ok(response) => {
2200 if let Some(choice) = response.choices.first() {
2201 let improved = choice.message.content.clone();
2202 let improved_clean = improved
2203 .strip_prefix("IMPROVED:")
2204 .unwrap_or(&improved)
2205 .trim()
2206 .to_string();
2207 current_solution = improved_clean;
2208 info!(
2209 round = round + 1,
2210 "Solution improved via {}: {} chars",
2211 improver.provider_name(),
2212 current_solution.len()
2213 );
2214 }
2215 }
2216 Err(e) => {
2217 warn!(error = %e, round = round + 1, "Multi improvement failed");
2218 }
2219 }
2220 }
2221 }
2222 }
2223
2224 println!(
2225 "\n🐦⬛ Self-Reflection (Multi) Final Solution (after {} rounds):\n{}",
2226 pattern_config.reflection_rounds, current_solution
2227 );
2228
2229 if let Some(ref rf) = ravenfabric {
2230 if rf.is_enabled() {
2231 let summary = format!(
2232 "Self-reflection (multi) completed: {} rounds, final solution {} chars",
2233 pattern_config.reflection_rounds,
2234 current_solution.len()
2235 );
2236 let _ = rf.broadcast(&summary, 30).await;
2237 }
2238 }
2239
2240 Ok(())
2241}
2242
2243#[cfg(test)]
2246mod tests {
2247 use super::*;
2248
2249 #[test]
2250 fn test_pattern_config_defaults() {
2251 let cfg = PatternConfig::default();
2252 assert_eq!(cfg.max_rounds, 3);
2253 assert_eq!(cfg.max_review_iterations, 3);
2254 assert_eq!(cfg.research_agent_count, 3);
2255 assert_eq!(cfg.voter_count, 3);
2256 assert!(!cfg.verbose);
2257 assert_eq!(cfg.tot_branches, 3);
2258 assert_eq!(cfg.tot_depth, 3);
2259 assert_eq!(cfg.tot_top_k, 2);
2260 assert_eq!(cfg.reflection_rounds, 2);
2261 }
2262
2263 #[test]
2264 fn test_pattern_config_custom() {
2265 let cfg = PatternConfig {
2266 max_rounds: 5,
2267 max_review_iterations: 5,
2268 research_agent_count: 5,
2269 voter_count: 7,
2270 verbose: true,
2271 tot_branches: 4,
2272 tot_depth: 5,
2273 tot_top_k: 3,
2274 reflection_rounds: 4,
2275 };
2276 assert_eq!(cfg.max_rounds, 5);
2277 assert_eq!(cfg.voter_count, 7);
2278 assert!(cfg.verbose);
2279 assert_eq!(cfg.tot_branches, 4);
2280 assert_eq!(cfg.tot_depth, 5);
2281 assert_eq!(cfg.tot_top_k, 3);
2282 assert_eq!(cfg.reflection_rounds, 4);
2283 }
2284
2285 #[test]
2286 fn test_debate_function_exists() {
2287 let _ = std::mem::size_of::<PatternConfig>();
2289 }
2290
2291 #[test]
2292 fn test_review_loop_function_exists() {
2293 let cfg = PatternConfig::default();
2294 assert_eq!(cfg.max_review_iterations, 3);
2295 }
2296
2297 #[test]
2298 fn test_research_synthesize_function_exists() {
2299 let cfg = PatternConfig::default();
2300 assert_eq!(cfg.research_agent_count, 3);
2301 }
2302
2303 #[test]
2304 fn test_voting_function_exists() {
2305 let cfg = PatternConfig::default();
2306 assert_eq!(cfg.voter_count, 3);
2307 }
2308
2309 #[test]
2310 fn test_tree_of_thought_config() {
2311 let cfg = PatternConfig::default();
2312 assert_eq!(cfg.tot_branches, 3);
2313 assert_eq!(cfg.tot_depth, 3);
2314 assert_eq!(cfg.tot_top_k, 2);
2315 }
2316
2317 #[test]
2318 fn test_self_reflection_config() {
2319 let cfg = PatternConfig::default();
2320 assert_eq!(cfg.reflection_rounds, 2);
2321 }
2322}