1use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use crate::architecture_simulator::{Architecture, SimulationMetrics};
12use crate::heuristic_decision::{DecisionLogEntry, HeuristicPattern};
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub enum KnowledgeEntryType {
19 ArchitecturePattern,
21 SimulationResult,
23 DecisionPattern,
25 Insight,
27 HeuristicRule,
29 BestPractice,
31 KnownPitfall,
33 Custom(String),
35}
36
37#[derive(Debug, Clone)]
39pub struct KnowledgeEntry {
40 pub id: String,
41 pub entry_type: KnowledgeEntryType,
42 pub title: String,
43 pub description: String,
44 pub tags: Vec<String>,
45 pub related_entries: Vec<String>,
47 pub confidence: f64,
49 pub usefulness: u64,
51 pub created_at: u64,
53 pub updated_at: u64,
55 pub source: String,
57 pub context: String,
59}
60
61#[derive(Debug, Clone)]
63pub struct SearchResult {
64 pub entry: KnowledgeEntry,
65 pub relevance_score: f64,
66 pub matched_tags: Vec<String>,
67}
68
69#[derive(Debug, Clone)]
71pub struct KnowledgeBaseStats {
72 pub total_entries: u64,
73 pub architecture_patterns: u64,
74 pub simulation_results: u64,
75 pub decision_patterns: u64,
76 pub insights: u64,
77 pub heuristic_rules: u64,
78 pub best_practices: u64,
79 pub known_pitfalls: u64,
80 pub avg_confidence: f64,
81 pub total_usefulness: u64,
82}
83
84pub struct KnowledgeBase {
88 entries: Arc<RwLock<HashMap<String, KnowledgeEntry>>>,
89 tag_index: Arc<RwLock<HashMap<String, Vec<String>>>>, type_index: Arc<RwLock<HashMap<KnowledgeEntryType, Vec<String>>>>, associations: Arc<RwLock<HashMap<String, HashMap<String, f64>>>>,
93 #[allow(dead_code)]
94 next_id: Arc<RwLock<u64>>,
95}
96
97impl Default for KnowledgeBase {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103impl KnowledgeBase {
104 pub fn new() -> Self {
106 Self {
107 entries: Arc::new(RwLock::new(HashMap::new())),
108 tag_index: Arc::new(RwLock::new(HashMap::new())),
109 type_index: Arc::new(RwLock::new(HashMap::new())),
110 associations: Arc::new(RwLock::new(HashMap::new())),
111 next_id: Arc::new(RwLock::new(1)),
112 }
113 }
114
115 pub fn add_entry(&self, entry: KnowledgeEntry) -> String {
117 let mut entries = crate::sync_guard::write_lock(&self.entries);
118 let mut tag_index = crate::sync_guard::write_lock(&self.tag_index);
119 let mut type_index = crate::sync_guard::write_lock(&self.type_index);
120
121 let id = entry.id.clone();
122 entries.insert(id.clone(), entry.clone());
123
124 for tag in &entry.tags {
126 tag_index.entry(tag.clone()).or_default().push(id.clone());
127 }
128
129 type_index
131 .entry(entry.entry_type.clone())
132 .or_default()
133 .push(id.clone());
134
135 id
136 }
137
138 pub fn get_entry(&self, id: &str) -> Option<KnowledgeEntry> {
140 let entries = crate::sync_guard::read_lock(&self.entries);
141 entries.get(id).cloned()
142 }
143
144 pub fn search(&self, query: &str, max_results: usize) -> Vec<SearchResult> {
146 let entries = crate::sync_guard::read_lock(&self.entries);
147 let query_lower = query.to_lowercase();
148 let query_words: Vec<&str> = query_lower.split_whitespace().collect();
149
150 let mut scored: Vec<(f64, &KnowledgeEntry, Vec<String>)> = entries
151 .values()
152 .map(|entry| {
153 let mut score = 0.0;
154
155 if entry.title.to_lowercase().contains(&query_lower) {
157 score += 3.0;
158 }
159
160 if entry.description.to_lowercase().contains(&query_lower) {
162 score += 2.0;
163 }
164
165 let matched_tags: Vec<String> = entry
167 .tags
168 .iter()
169 .filter(|tag| query_words.iter().any(|w| tag.to_lowercase().contains(w)))
170 .cloned()
171 .collect();
172 score += matched_tags.len() as f64 * 1.5;
173
174 if entry.context.to_lowercase().contains(&query_lower) {
176 score += 1.0;
177 }
178
179 score += (entry.usefulness as f64).ln_1p() * 0.5;
181
182 score += entry.confidence * 0.5;
184
185 (score, entry, matched_tags)
186 })
187 .filter(|(score, _, _)| *score > 0.0)
188 .collect();
189
190 scored.sort_by(|a, b| b.0.total_cmp(&a.0));
192
193 scored
194 .into_iter()
195 .take(max_results)
196 .map(|(score, entry, matched_tags)| SearchResult {
197 entry: entry.clone(),
198 relevance_score: score,
199 matched_tags,
200 })
201 .collect()
202 }
203
204 pub fn mark_useful(&self, id: &str) {
206 let mut entries = crate::sync_guard::write_lock(&self.entries);
207 if let Some(entry) = entries.get_mut(id) {
208 entry.usefulness += 1;
209 entry.updated_at = SystemTime::now()
210 .duration_since(UNIX_EPOCH)
211 .unwrap()
212 .as_secs();
213 }
214 }
215
216 pub fn learn_association(&self, from_id: &str, to_id: &str, weight: f64) {
218 let mut associations = crate::sync_guard::write_lock(&self.associations);
219 associations
220 .entry(from_id.to_string())
221 .or_default()
222 .entry(to_id.to_string())
223 .and_modify(|w| *w = (*w + weight) / 2.0) .or_insert(weight);
225 }
226
227 pub fn get_related(&self, id: &str, max_results: usize) -> Vec<KnowledgeEntry> {
229 let associations = crate::sync_guard::read_lock(&self.associations);
230 let entries = crate::sync_guard::read_lock(&self.entries);
231
232 if let Some(related) = associations.get(id) {
233 let mut scored: Vec<(f64, &KnowledgeEntry)> = related
234 .iter()
235 .filter_map(|(rel_id, weight)| entries.get(rel_id).map(|entry| (*weight, entry)))
236 .collect();
237
238 scored.sort_by(|a, b| b.0.total_cmp(&a.0));
239
240 scored
241 .into_iter()
242 .take(max_results)
243 .map(|(_, entry)| entry.clone())
244 .collect()
245 } else {
246 Vec::new()
247 }
248 }
249
250 pub fn add_from_simulation(&self, arch: &Architecture, metrics: &SimulationMetrics) -> String {
252 let now = SystemTime::now()
253 .duration_since(UNIX_EPOCH)
254 .unwrap()
255 .as_secs();
256
257 let mut tags = vec![
258 "architecture".to_string(),
259 arch.name.clone(),
260 format!("components_{}", arch.components.len()),
261 format!("connections_{}", arch.connections.len()),
262 ];
263
264 if !metrics.bottleneck_components.is_empty() {
265 tags.push("bottleneck".to_string());
266 for b in &metrics.bottleneck_components {
267 tags.push(format!("bottleneck_{}", b));
268 }
269 }
270
271 if metrics.stability_score > 0.8 {
272 tags.push("high_stability".to_string());
273 }
274 if metrics.resilience_score > 0.8 {
275 tags.push("high_resilience".to_string());
276 }
277
278 let entry = KnowledgeEntry {
279 id: format!("sim_{}", rand::random::<u32>()),
280 entry_type: KnowledgeEntryType::SimulationResult,
281 title: format!("Simulation: {} (stability={:.2}, resilience={:.2})",
282 arch.name, metrics.stability_score, metrics.resilience_score),
283 description: format!(
284 "Architecture '{}' simulated: avg_latency={:.2}ms, throughput={:.0}req/s, error_rate={:.2}%, bottlenecks={}",
285 arch.name, metrics.avg_latency_ms, metrics.throughput_req_per_sec,
286 metrics.error_rate * 100.0, metrics.bottleneck_components.len()
287 ),
288 tags,
289 related_entries: Vec::new(),
290 confidence: metrics.stability_score.min(metrics.resilience_score),
291 usefulness: 0,
292 created_at: now,
293 updated_at: now,
294 source: format!("simulation_{}", arch.id),
295 context: format!("load_pattern=standard, duration={}s", metrics.simulation_time_secs),
296 };
297
298 self.add_entry(entry)
299 }
300
301 pub fn add_from_decision(
303 &self,
304 decision: &DecisionLogEntry,
305 pattern: &HeuristicPattern,
306 ) -> String {
307 let now = SystemTime::now()
308 .duration_since(UNIX_EPOCH)
309 .unwrap()
310 .as_secs();
311
312 let entry = KnowledgeEntry {
313 id: format!("decision_{}", rand::random::<u32>()),
314 entry_type: KnowledgeEntryType::DecisionPattern,
315 title: format!(
316 "Decision pattern: {} (success={:.1}%)",
317 pattern.name,
318 pattern.success_rate * 100.0
319 ),
320 description: format!(
321 "Decision type: {:?}, selected: {}, outcome_score: {:.2}, reflection: {}",
322 decision.decision_type,
323 decision.selected_option,
324 decision.outcome_score,
325 decision.reflection
326 ),
327 tags: vec![
328 "decision".to_string(),
329 format!("{:?}", decision.decision_type),
330 format!("success_{:.0}", pattern.success_rate * 100.0),
331 ],
332 related_entries: Vec::new(),
333 confidence: pattern.success_rate,
334 usefulness: pattern.usage_count,
335 created_at: now,
336 updated_at: now,
337 source: format!("decision_{}", decision.decision_id),
338 context: pattern.context.clone(),
339 };
340
341 self.add_entry(entry)
342 }
343
344 pub fn add_insight(
346 &self,
347 title: &str,
348 description: &str,
349 tags: Vec<String>,
350 source: &str,
351 ) -> String {
352 let now = SystemTime::now()
353 .duration_since(UNIX_EPOCH)
354 .unwrap()
355 .as_secs();
356
357 let entry = KnowledgeEntry {
358 id: format!("insight_{}", rand::random::<u32>()),
359 entry_type: KnowledgeEntryType::Insight,
360 title: title.to_string(),
361 description: description.to_string(),
362 tags: {
363 let mut t = vec!["insight".to_string(), "eureka".to_string()];
364 t.extend(tags);
365 t
366 },
367 related_entries: Vec::new(),
368 confidence: 0.5, usefulness: 0,
370 created_at: now,
371 updated_at: now,
372 source: source.to_string(),
373 context: "discovery".to_string(),
374 };
375
376 self.add_entry(entry)
377 }
378
379 pub fn add_best_practice(
381 &self,
382 title: &str,
383 description: &str,
384 context: &str,
385 tags: Vec<String>,
386 ) -> String {
387 let now = SystemTime::now()
388 .duration_since(UNIX_EPOCH)
389 .unwrap()
390 .as_secs();
391
392 let entry = KnowledgeEntry {
393 id: format!("practice_{}", rand::random::<u32>()),
394 entry_type: KnowledgeEntryType::BestPractice,
395 title: title.to_string(),
396 description: description.to_string(),
397 tags: {
398 let mut t = vec!["best_practice".to_string(), "recommendation".to_string()];
399 t.extend(tags);
400 t
401 },
402 related_entries: Vec::new(),
403 confidence: 0.7,
404 usefulness: 0,
405 created_at: now,
406 updated_at: now,
407 source: "knowledge_base".to_string(),
408 context: context.to_string(),
409 };
410
411 self.add_entry(entry)
412 }
413
414 pub fn add_pitfall(
416 &self,
417 title: &str,
418 description: &str,
419 context: &str,
420 tags: Vec<String>,
421 ) -> String {
422 let now = SystemTime::now()
423 .duration_since(UNIX_EPOCH)
424 .unwrap()
425 .as_secs();
426
427 let entry = KnowledgeEntry {
428 id: format!("pitfall_{}", rand::random::<u32>()),
429 entry_type: KnowledgeEntryType::KnownPitfall,
430 title: title.to_string(),
431 description: description.to_string(),
432 tags: {
433 let mut t = vec![
434 "pitfall".to_string(),
435 "warning".to_string(),
436 "anti_pattern".to_string(),
437 ];
438 t.extend(tags);
439 t
440 },
441 related_entries: Vec::new(),
442 confidence: 0.8,
443 usefulness: 0,
444 created_at: now,
445 updated_at: now,
446 source: "knowledge_base".to_string(),
447 context: context.to_string(),
448 };
449
450 self.add_entry(entry)
451 }
452
453 pub fn add_heuristic_rule(
455 &self,
456 title: &str,
457 description: &str,
458 context: &str,
459 confidence: f64,
460 tags: Vec<String>,
461 ) -> String {
462 let now = SystemTime::now()
463 .duration_since(UNIX_EPOCH)
464 .unwrap()
465 .as_secs();
466
467 let entry = KnowledgeEntry {
468 id: format!("rule_{}", rand::random::<u32>()),
469 entry_type: KnowledgeEntryType::HeuristicRule,
470 title: title.to_string(),
471 description: description.to_string(),
472 tags: {
473 let mut t = vec!["heuristic".to_string(), "rule".to_string()];
474 t.extend(tags);
475 t
476 },
477 related_entries: Vec::new(),
478 confidence,
479 usefulness: 0,
480 created_at: now,
481 updated_at: now,
482 source: "knowledge_base".to_string(),
483 context: context.to_string(),
484 };
485
486 self.add_entry(entry)
487 }
488
489 pub fn get_by_type(&self, entry_type: &KnowledgeEntryType) -> Vec<KnowledgeEntry> {
491 let type_index = crate::sync_guard::read_lock(&self.type_index);
492 let entries = crate::sync_guard::read_lock(&self.entries);
493
494 type_index
495 .get(entry_type)
496 .map(|ids| {
497 ids.iter()
498 .filter_map(|id| entries.get(id))
499 .cloned()
500 .collect()
501 })
502 .unwrap_or_default()
503 }
504
505 pub fn get_by_tag(&self, tag: &str) -> Vec<KnowledgeEntry> {
507 let tag_index = crate::sync_guard::read_lock(&self.tag_index);
508 let entries = crate::sync_guard::read_lock(&self.entries);
509
510 tag_index
511 .get(tag)
512 .map(|ids| {
513 ids.iter()
514 .filter_map(|id| entries.get(id))
515 .cloned()
516 .collect()
517 })
518 .unwrap_or_default()
519 }
520
521 pub fn get_stats(&self) -> KnowledgeBaseStats {
523 let entries = crate::sync_guard::read_lock(&self.entries);
524 let type_index = crate::sync_guard::read_lock(&self.type_index);
525
526 let total = entries.len() as u64;
527 let arch_count = type_index
528 .get(&KnowledgeEntryType::ArchitecturePattern)
529 .map(|v| v.len() as u64)
530 .unwrap_or(0);
531 let sim_count = type_index
532 .get(&KnowledgeEntryType::SimulationResult)
533 .map(|v| v.len() as u64)
534 .unwrap_or(0);
535 let dec_count = type_index
536 .get(&KnowledgeEntryType::DecisionPattern)
537 .map(|v| v.len() as u64)
538 .unwrap_or(0);
539 let ins_count = type_index
540 .get(&KnowledgeEntryType::Insight)
541 .map(|v| v.len() as u64)
542 .unwrap_or(0);
543 let rule_count = type_index
544 .get(&KnowledgeEntryType::HeuristicRule)
545 .map(|v| v.len() as u64)
546 .unwrap_or(0);
547 let prac_count = type_index
548 .get(&KnowledgeEntryType::BestPractice)
549 .map(|v| v.len() as u64)
550 .unwrap_or(0);
551 let pit_count = type_index
552 .get(&KnowledgeEntryType::KnownPitfall)
553 .map(|v| v.len() as u64)
554 .unwrap_or(0);
555
556 let avg_conf = if total > 0 {
557 entries.values().map(|e| e.confidence).sum::<f64>() / total as f64
558 } else {
559 0.0
560 };
561
562 let total_use = entries.values().map(|e| e.usefulness).sum();
563
564 KnowledgeBaseStats {
565 total_entries: total,
566 architecture_patterns: arch_count,
567 simulation_results: sim_count,
568 decision_patterns: dec_count,
569 insights: ins_count,
570 heuristic_rules: rule_count,
571 best_practices: prac_count,
572 known_pitfalls: pit_count,
573 avg_confidence: avg_conf,
574 total_usefulness: total_use,
575 }
576 }
577
578 pub fn export_all(&self) -> Vec<KnowledgeEntry> {
580 let entries = crate::sync_guard::read_lock(&self.entries);
581 entries.values().cloned().collect()
582 }
583
584 pub fn clear(&self) {
586 crate::sync_guard::write_lock(&self.entries).clear();
587 crate::sync_guard::write_lock(&self.tag_index).clear();
588 crate::sync_guard::write_lock(&self.type_index).clear();
589 crate::sync_guard::write_lock(&self.associations).clear();
590 }
591}
592
593pub fn auto_build_from_simulations(
597 kb: &KnowledgeBase,
598 architectures: &[Architecture],
599 simulation_results: &HashMap<String, Vec<SimulationMetrics>>,
600) {
601 for arch in architectures {
602 if let Some(metrics_list) = simulation_results.get(&arch.id) {
603 for metrics in metrics_list {
604 let _entry_id = kb.add_from_simulation(arch, metrics);
605
606 if !metrics.bottleneck_components.is_empty() {
608 kb.add_pitfall(
609 &format!("Bottleneck in {}: {}", arch.name, metrics.bottleneck_components.join(", ")),
610 &format!("Components {} became bottlenecks at load. Consider scaling or optimizing.",
611 metrics.bottleneck_components.join(", ")),
612 &format!("architecture={}, load_pattern=standard", arch.name),
613 vec![
614 "bottleneck".to_string(),
615 arch.name.clone(),
616 "performance".to_string(),
617 ],
618 );
619 }
620
621 if metrics.stability_score > 0.9 {
623 kb.add_best_practice(
624 &format!("High stability architecture: {}", arch.name),
625 &format!(
626 "Architecture '{}' achieved {:.1}% stability under test conditions.",
627 arch.name,
628 metrics.stability_score * 100.0
629 ),
630 &format!("architecture={}, stability>0.9", arch.name),
631 vec![
632 "stability".to_string(),
633 arch.name.clone(),
634 "reference_architecture".to_string(),
635 ],
636 );
637 }
638 }
639 }
640 }
641}
642
643pub fn auto_build_from_decisions(
645 kb: &KnowledgeBase,
646 decision_log: &[DecisionLogEntry],
647 patterns: &[HeuristicPattern],
648) {
649 for pattern in patterns {
650 let relevant_decisions: Vec<&DecisionLogEntry> = decision_log
652 .iter()
653 .filter(|d| format!("{:?}", d.decision_type) == pattern.pattern_type)
654 .collect();
655
656 if let Some(latest) = relevant_decisions.last() {
657 kb.add_from_decision(latest, pattern);
658 }
659
660 if pattern.success_rate > 0.8 && pattern.usage_count > 3 {
662 kb.add_best_practice(
663 &format!("Proven decision pattern: {}", pattern.name),
664 &format!(
665 "Pattern '{}' has {:.1}% success rate after {} uses.",
666 pattern.name,
667 pattern.success_rate * 100.0,
668 pattern.usage_count
669 ),
670 &format!("decision_type={}", pattern.pattern_type),
671 vec![
672 "decision".to_string(),
673 pattern.pattern_type.clone(),
674 "proven".to_string(),
675 ],
676 );
677 }
678
679 if pattern.success_rate < 0.3 && pattern.usage_count > 2 {
681 kb.add_pitfall(
682 &format!("Unreliable decision pattern: {}", pattern.name),
683 &format!(
684 "Pattern '{}' has only {:.1}% success rate. Consider alternative approaches.",
685 pattern.name,
686 pattern.success_rate * 100.0
687 ),
688 &format!("decision_type={}", pattern.pattern_type),
689 vec![
690 "decision".to_string(),
691 pattern.pattern_type.clone(),
692 "unreliable".to_string(),
693 ],
694 );
695 }
696 }
697}
698
699pub fn auto_learn_associations(kb: &KnowledgeBase) {
701 let entries = kb.export_all();
702
703 for i in 0..entries.len() {
704 for j in (i + 1)..entries.len() {
705 let a = &entries[i];
706 let b = &entries[j];
707
708 let common_tags: Vec<&String> = a.tags.iter().filter(|t| b.tags.contains(t)).collect();
710
711 if !common_tags.is_empty() {
712 let weight =
713 common_tags.len() as f64 / a.tags.len().clamp(1, b.tags.len().max(1)) as f64;
714 if weight > 0.3 {
715 kb.learn_association(&a.id, &b.id, weight);
716 kb.learn_association(&b.id, &a.id, weight);
717 }
718 }
719
720 if a.entry_type == KnowledgeEntryType::SimulationResult
722 && b.entry_type == KnowledgeEntryType::KnownPitfall
723 && a.description.contains(&b.title[..b.title.len().min(20)])
724 {
725 kb.learn_association(&a.id, &b.id, 0.8);
726 }
727 }
728 }
729}
730
731#[cfg(test)]
732mod tests {
733 use super::*;
734
735 #[test]
736 fn test_add_and_search() {
737 let kb = KnowledgeBase::new();
738
739 kb.add_best_practice(
740 "Use microservices for scalability",
741 "Microservices allow independent scaling of components",
742 "high_load_systems",
743 vec!["microservices".to_string(), "scalability".to_string()],
744 );
745
746 kb.add_pitfall(
747 "Avoid tight coupling in distributed systems",
748 "Tight coupling reduces fault tolerance",
749 "distributed_systems",
750 vec!["coupling".to_string(), "distributed".to_string()],
751 );
752
753 let results = kb.search("microservices", 10);
754 assert!(!results.is_empty());
755 assert!(results[0].relevance_score > 0.0);
756 }
757
758 #[test]
759 fn test_get_stats() {
760 let kb = KnowledgeBase::new();
761 let stats = kb.get_stats();
762 assert_eq!(stats.total_entries, 0);
763
764 kb.add_best_practice("Test", "Test description", "test", vec![]);
765 let stats = kb.get_stats();
766 assert_eq!(stats.total_entries, 1);
767 assert_eq!(stats.best_practices, 1);
768 }
769
770 #[test]
771 fn test_auto_associations() {
772 let kb = KnowledgeBase::new();
773
774 let id_a = kb.add_best_practice(
775 "Test Practice",
776 "A test best practice",
777 "test",
778 vec!["test".to_string(), "practice".to_string()],
779 );
780
781 let id_b = kb.add_pitfall(
782 "Test Pitfall",
783 "A test pitfall",
784 "test",
785 vec!["test".to_string(), "pitfall".to_string()],
786 );
787
788 auto_learn_associations(&kb);
789 kb.learn_association(&id_a, &id_b, 0.5);
790
791 let related = kb.get_related(&id_a, 5);
792 assert!(!related.is_empty());
793 }
794}