1use eml_core::{EmlEvent, EmlModel};
19use serde::{Deserialize, Serialize};
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct GovernanceScorerModel {
33 inner: EmlModel,
34}
35
36impl Default for GovernanceScorerModel {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl GovernanceScorerModel {
43 pub fn new() -> Self {
45 let mut inner = EmlModel::new(3, 5, 1);
46 inner.set_model_name("governance_scorer");
47 Self { inner }
48 }
49
50 pub fn drain_events(&mut self) -> Vec<EmlEvent> {
52 self.inner.drain_events()
53 }
54
55 pub fn is_trained(&self) -> bool {
57 self.inner.is_trained()
58 }
59
60 pub fn training_sample_count(&self) -> usize {
62 self.inner.training_sample_count()
63 }
64
65 pub fn predict(
69 &self,
70 risk: f64,
71 fairness: f64,
72 privacy: f64,
73 novelty: f64,
74 security: f64,
75 ) -> f64 {
76 if !self.inner.is_trained() {
77 return (risk * risk
78 + fairness * fairness
79 + privacy * privacy
80 + novelty * novelty
81 + security * security)
82 .sqrt();
83 }
84 let inputs = [risk, fairness, privacy, novelty, security];
85 self.inner.predict_primary(&inputs).max(0.0)
86 }
87
88 pub fn record(
92 &mut self,
93 risk: f64,
94 fairness: f64,
95 privacy: f64,
96 novelty: f64,
97 security: f64,
98 score: f64,
99 ) {
100 let inputs = [risk, fairness, privacy, novelty, security];
101 self.inner.record(&inputs, &[Some(score)]);
102 }
103
104 pub fn train(&mut self) -> bool {
108 self.inner.train()
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct RestartStrategyModel {
125 inner: EmlModel,
126}
127
128impl Default for RestartStrategyModel {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl RestartStrategyModel {
135 pub fn new() -> Self {
137 let mut inner = EmlModel::new(2, 4, 2);
138 inner.set_model_name("restart_strategy");
139 Self { inner }
140 }
141
142 pub fn drain_events(&mut self) -> Vec<EmlEvent> {
144 self.inner.drain_events()
145 }
146
147 pub fn is_trained(&self) -> bool {
149 self.inner.is_trained()
150 }
151
152 pub fn training_sample_count(&self) -> usize {
154 self.inner.training_sample_count()
155 }
156
157 pub fn predict(
165 &self,
166 failure_count: u32,
167 failure_type: u32,
168 uptime_before_failure_secs: f64,
169 system_load: f64,
170 ) -> (u64, bool) {
171 if !self.inner.is_trained() {
172 let base: u64 = 100;
173 let exponent = failure_count.saturating_sub(1);
174 let delay = base.saturating_mul(1u64 << exponent.min(20));
175 return (delay.min(30_000), true);
176 }
177
178 let inputs = [
179 failure_count as f64,
180 failure_type as f64,
181 uptime_before_failure_secs,
182 system_load,
183 ];
184 let heads = self.inner.predict(&inputs);
185 let delay_ms = (heads[0].max(0.0) * 1000.0) as u64;
186 let should_retry = heads[1] > 0.5;
187 (delay_ms.min(60_000), should_retry)
188 }
189
190 pub fn record(
195 &mut self,
196 failure_count: u32,
197 failure_type: u32,
198 uptime_before_failure_secs: f64,
199 system_load: f64,
200 actual_delay_ms: u64,
201 recovery_succeeded: bool,
202 ) {
203 let inputs = [
204 failure_count as f64,
205 failure_type as f64,
206 uptime_before_failure_secs,
207 system_load,
208 ];
209 let targets = [
210 Some(actual_delay_ms as f64 / 1000.0),
211 Some(if recovery_succeeded { 1.0 } else { 0.0 }),
212 ];
213 self.inner.record(&inputs, &targets);
214 }
215
216 pub fn train(&mut self) -> bool {
218 self.inner.train()
219 }
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct HealthThresholdModel {
234 inner: EmlModel,
235}
236
237impl Default for HealthThresholdModel {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243impl HealthThresholdModel {
244 pub fn new() -> Self {
246 let mut inner = EmlModel::new(2, 3, 2);
247 inner.set_model_name("health_threshold");
248 Self { inner }
249 }
250
251 pub fn drain_events(&mut self) -> Vec<EmlEvent> {
253 self.inner.drain_events()
254 }
255
256 pub fn is_trained(&self) -> bool {
258 self.inner.is_trained()
259 }
260
261 pub fn training_sample_count(&self) -> usize {
263 self.inner.training_sample_count()
264 }
265
266 pub fn predict(
272 &self,
273 service_type: u32,
274 history_depth: u32,
275 recent_latency_ms: f64,
276 ) -> (u32, u32) {
277 if !self.inner.is_trained() {
278 return (1, 3);
279 }
280
281 let inputs = [
282 service_type as f64,
283 history_depth as f64,
284 recent_latency_ms / 1000.0, ];
286 let heads = self.inner.predict(&inputs);
287 let degraded = heads[0].clamp(1.0, 20.0).round() as u32;
288 let failed = heads[1].clamp(1.0, 20.0).round() as u32;
289 (degraded, failed.max(degraded))
291 }
292
293 pub fn record(
298 &mut self,
299 service_type: u32,
300 history_depth: u32,
301 recent_latency_ms: f64,
302 optimal_degraded: u32,
303 optimal_failed: u32,
304 ) {
305 let inputs = [
306 service_type as f64,
307 history_depth as f64,
308 recent_latency_ms / 1000.0,
309 ];
310 let targets = [Some(optimal_degraded as f64), Some(optimal_failed as f64)];
311 self.inner.record(&inputs, &targets);
312 }
313
314 pub fn train(&mut self) -> bool {
316 self.inner.train()
317 }
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct DeadLetterModel {
332 inner: EmlModel,
333}
334
335impl Default for DeadLetterModel {
336 fn default() -> Self {
337 Self::new()
338 }
339}
340
341impl DeadLetterModel {
342 pub fn new() -> Self {
344 let mut inner = EmlModel::new(2, 3, 2);
345 inner.set_model_name("dead_letter");
346 Self { inner }
347 }
348
349 pub fn drain_events(&mut self) -> Vec<EmlEvent> {
351 self.inner.drain_events()
352 }
353
354 pub fn is_trained(&self) -> bool {
356 self.inner.is_trained()
357 }
358
359 pub fn training_sample_count(&self) -> usize {
361 self.inner.training_sample_count()
362 }
363
364 pub fn predict(
369 &self,
370 retry_count: u32,
371 message_age_ms: u64,
372 queue_depth: usize,
373 ) -> (u64, bool) {
374 if !self.inner.is_trained() {
375 let delay = (1000u64).saturating_mul(1u64 << retry_count.min(20));
376 return (delay.min(60_000), retry_count >= 5);
377 }
378
379 let inputs = [
380 retry_count as f64,
381 message_age_ms as f64 / 1000.0, queue_depth as f64,
383 ];
384 let heads = self.inner.predict(&inputs);
385 let delay_ms = (heads[0].max(0.0) * 1000.0) as u64;
386 let should_discard = heads[1] > 0.5;
387 (delay_ms.min(120_000), should_discard)
388 }
389
390 pub fn record(
395 &mut self,
396 retry_count: u32,
397 message_age_ms: u64,
398 queue_depth: usize,
399 actual_delay_ms: u64,
400 delivery_succeeded: bool,
401 ) {
402 let inputs = [
403 retry_count as f64,
404 message_age_ms as f64 / 1000.0,
405 queue_depth as f64,
406 ];
407 let targets = [
408 Some(actual_delay_ms as f64 / 1000.0),
409 Some(if delivery_succeeded { 0.0 } else { 1.0 }), ];
411 self.inner.record(&inputs, &targets);
412 }
413
414 pub fn train(&mut self) -> bool {
416 self.inner.train()
417 }
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct GossipTimingModel {
432 inner: EmlModel,
433}
434
435impl Default for GossipTimingModel {
436 fn default() -> Self {
437 Self::new()
438 }
439}
440
441impl GossipTimingModel {
442 pub fn new() -> Self {
444 let mut inner = EmlModel::new(2, 3, 1);
445 inner.set_model_name("gossip_timing");
446 Self { inner }
447 }
448
449 pub fn drain_events(&mut self) -> Vec<EmlEvent> {
451 self.inner.drain_events()
452 }
453
454 pub fn is_trained(&self) -> bool {
456 self.inner.is_trained()
457 }
458
459 pub fn training_sample_count(&self) -> usize {
461 self.inner.training_sample_count()
462 }
463
464 pub fn predict(
470 &self,
471 peer_count: usize,
472 network_latency_ms: f64,
473 update_frequency_hz: f64,
474 ) -> f64 {
475 if !self.inner.is_trained() {
476 return 5.0;
477 }
478
479 let inputs = [
480 peer_count as f64,
481 network_latency_ms / 1000.0, update_frequency_hz,
483 ];
484 self.inner.predict_primary(&inputs).clamp(1.0, 60.0)
485 }
486
487 pub fn record(
492 &mut self,
493 peer_count: usize,
494 network_latency_ms: f64,
495 update_frequency_hz: f64,
496 optimal_interval_secs: f64,
497 ) {
498 let inputs = [
499 peer_count as f64,
500 network_latency_ms / 1000.0,
501 update_frequency_hz,
502 ];
503 self.inner.record(&inputs, &[Some(optimal_interval_secs)]);
504 }
505
506 pub fn train(&mut self) -> bool {
508 self.inner.train()
509 }
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
532pub struct TickIntervalModel {
533 inner: EmlModel,
534}
535
536impl Default for TickIntervalModel {
537 fn default() -> Self {
538 Self::new()
539 }
540}
541
542impl TickIntervalModel {
543 pub fn new() -> Self {
545 let mut inner = EmlModel::new(2, 3, 1);
546 inner.set_model_name("tick_interval");
547 Self { inner }
548 }
549
550 pub fn drain_events(&mut self) -> Vec<EmlEvent> {
552 self.inner.drain_events()
553 }
554
555 pub fn is_trained(&self) -> bool {
557 self.inner.is_trained()
558 }
559
560 pub fn training_sample_count(&self) -> usize {
562 self.inner.training_sample_count()
563 }
564
565 pub fn predict(&self, cpm: f64, idle_ticks: u64, variance: f64) -> u32 {
570 if !self.inner.is_trained() {
571 return 1000;
572 }
573 let inputs = [cpm, idle_ticks as f64, variance];
574 let raw = self.inner.predict_primary(&inputs);
575 raw.clamp(100.0, 60_000.0).round() as u32
576 }
577
578 pub fn recommend_or(&self, cpm: f64, idle_ticks: u64, variance: f64, fallback: u32) -> u32 {
582 if self.inner.is_trained() {
583 self.predict(cpm, idle_ticks, variance)
584 } else {
585 fallback
586 }
587 }
588
589 pub fn record(&mut self, cpm: f64, idle_ticks: u64, variance: f64, recommended_ms: u32) {
592 let inputs = [cpm, idle_ticks as f64, variance];
593 self.inner.record(&inputs, &[Some(recommended_ms as f64)]);
594 }
595
596 pub fn train(&mut self) -> bool {
598 self.inner.train()
599 }
600}
601
602#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct ComplexityModel {
614 inner: EmlModel,
615}
616
617impl Default for ComplexityModel {
618 fn default() -> Self {
619 Self::new()
620 }
621}
622
623impl ComplexityModel {
624 pub fn new() -> Self {
626 let mut inner = EmlModel::new(2, 3, 1);
627 inner.set_model_name("complexity");
628 Self { inner }
629 }
630
631 pub fn drain_events(&mut self) -> Vec<EmlEvent> {
633 self.inner.drain_events()
634 }
635
636 pub fn is_trained(&self) -> bool {
638 self.inner.is_trained()
639 }
640
641 pub fn training_sample_count(&self) -> usize {
643 self.inner.training_sample_count()
644 }
645
646 pub fn predict(&self, language_type: u32, avg_file_size: f64, team_size_proxy: f64) -> usize {
652 if !self.inner.is_trained() {
653 return 500;
654 }
655
656 let inputs = [
657 language_type as f64,
658 avg_file_size / 1000.0, team_size_proxy,
660 ];
661 let raw = self.inner.predict_primary(&inputs);
662 (raw.clamp(100.0, 5000.0)).round() as usize
663 }
664
665 pub fn record(
670 &mut self,
671 language_type: u32,
672 avg_file_size: f64,
673 team_size_proxy: f64,
674 optimal_threshold: usize,
675 ) {
676 let inputs = [
677 language_type as f64,
678 avg_file_size / 1000.0,
679 team_size_proxy,
680 ];
681 self.inner
682 .record(&inputs, &[Some(optimal_threshold as f64)]);
683 }
684
685 pub fn train(&mut self) -> bool {
687 self.inner.train()
688 }
689}
690
691#[cfg(test)]
696mod tests {
697 use super::*;
698
699 #[test]
702 fn governance_scorer_fallback_matches_l2_norm() {
703 let model = GovernanceScorerModel::new();
704 assert!(!model.is_trained());
705
706 let score = model.predict(0.5, 0.3, 0.2, 0.1, 0.4);
707 let expected = (0.5_f64.powi(2)
708 + 0.3_f64.powi(2)
709 + 0.2_f64.powi(2)
710 + 0.1_f64.powi(2)
711 + 0.4_f64.powi(2))
712 .sqrt();
713 assert!((score - expected).abs() < 1e-10);
714 }
715
716 #[test]
717 fn governance_scorer_zero_vector() {
718 let model = GovernanceScorerModel::new();
719 assert_eq!(model.predict(0.0, 0.0, 0.0, 0.0, 0.0), 0.0);
720 }
721
722 #[test]
723 fn governance_scorer_record_increments_count() {
724 let mut model = GovernanceScorerModel::new();
725 assert_eq!(model.training_sample_count(), 0);
726 model.record(0.5, 0.3, 0.2, 0.1, 0.4, 0.75);
727 assert_eq!(model.training_sample_count(), 1);
728 }
729
730 #[test]
731 fn governance_scorer_serde_roundtrip() {
732 let model = GovernanceScorerModel::new();
733 let json = serde_json::to_string(&model).unwrap();
734 let restored: GovernanceScorerModel = serde_json::from_str(&json).unwrap();
735 assert!(!restored.is_trained());
736 assert_eq!(restored.predict(1.0, 0.0, 0.0, 0.0, 0.0), 1.0);
737 }
738
739 #[test]
742 fn restart_strategy_fallback_exponential_backoff() {
743 let model = RestartStrategyModel::new();
744 assert!(!model.is_trained());
745
746 let (delay, retry) = model.predict(1, 0, 60.0, 0.5);
748 assert_eq!(delay, 100);
749 assert!(retry);
750
751 let (delay, _) = model.predict(3, 0, 60.0, 0.5);
753 assert_eq!(delay, 400);
754
755 let (delay, _) = model.predict(10, 0, 60.0, 0.5);
757 assert_eq!(delay, 30_000);
758 }
759
760 #[test]
761 fn restart_strategy_record_increments_count() {
762 let mut model = RestartStrategyModel::new();
763 model.record(1, 0, 60.0, 0.5, 100, true);
764 assert_eq!(model.training_sample_count(), 1);
765 }
766
767 #[test]
768 fn restart_strategy_serde_roundtrip() {
769 let model = RestartStrategyModel::new();
770 let json = serde_json::to_string(&model).unwrap();
771 let restored: RestartStrategyModel = serde_json::from_str(&json).unwrap();
772 assert!(!restored.is_trained());
773 }
774
775 #[test]
778 fn health_threshold_fallback_defaults() {
779 let model = HealthThresholdModel::new();
780 assert!(!model.is_trained());
781
782 let (degraded, failed) = model.predict(0, 100, 50.0);
783 assert_eq!(degraded, 1);
784 assert_eq!(failed, 3);
785 }
786
787 #[test]
788 fn health_threshold_record_increments_count() {
789 let mut model = HealthThresholdModel::new();
790 model.record(0, 100, 50.0, 2, 5);
791 assert_eq!(model.training_sample_count(), 1);
792 }
793
794 #[test]
795 fn health_threshold_serde_roundtrip() {
796 let model = HealthThresholdModel::new();
797 let json = serde_json::to_string(&model).unwrap();
798 let restored: HealthThresholdModel = serde_json::from_str(&json).unwrap();
799 assert!(!restored.is_trained());
800 }
801
802 #[test]
805 fn dead_letter_fallback_exponential() {
806 let model = DeadLetterModel::new();
807 assert!(!model.is_trained());
808
809 let (delay, discard) = model.predict(0, 5000, 100);
811 assert_eq!(delay, 1000);
812 assert!(!discard);
813
814 let (delay, discard) = model.predict(3, 10000, 100);
816 assert_eq!(delay, 8000);
817 assert!(!discard);
818
819 let (_, discard) = model.predict(5, 30000, 100);
821 assert!(discard);
822
823 let (delay, _) = model.predict(7, 60000, 100);
825 assert_eq!(delay, 60_000);
826 }
827
828 #[test]
829 fn dead_letter_record_increments_count() {
830 let mut model = DeadLetterModel::new();
831 model.record(1, 5000, 50, 2000, true);
832 assert_eq!(model.training_sample_count(), 1);
833 }
834
835 #[test]
836 fn dead_letter_serde_roundtrip() {
837 let model = DeadLetterModel::new();
838 let json = serde_json::to_string(&model).unwrap();
839 let restored: DeadLetterModel = serde_json::from_str(&json).unwrap();
840 assert!(!restored.is_trained());
841 }
842
843 #[test]
846 fn gossip_timing_fallback_5s() {
847 let model = GossipTimingModel::new();
848 assert!(!model.is_trained());
849
850 let interval = model.predict(10, 50.0, 1.0);
851 assert!((interval - 5.0).abs() < 1e-10);
852 }
853
854 #[test]
855 fn gossip_timing_record_increments_count() {
856 let mut model = GossipTimingModel::new();
857 model.record(10, 50.0, 1.0, 3.0);
858 assert_eq!(model.training_sample_count(), 1);
859 }
860
861 #[test]
862 fn gossip_timing_serde_roundtrip() {
863 let model = GossipTimingModel::new();
864 let json = serde_json::to_string(&model).unwrap();
865 let restored: GossipTimingModel = serde_json::from_str(&json).unwrap();
866 assert!(!restored.is_trained());
867 }
868
869 #[test]
872 fn tick_interval_fallback_returns_1000ms() {
873 let model = TickIntervalModel::new();
874 assert!(!model.is_trained());
875 assert_eq!(model.predict(5.0, 10, 0.2), 1000);
876 }
877
878 #[test]
879 fn tick_interval_recommend_or_uses_fallback_when_untrained() {
880 let model = TickIntervalModel::new();
881 assert_eq!(model.recommend_or(5.0, 10, 0.2, 1234), 1234);
883 }
884
885 #[test]
886 fn tick_interval_record_increments_count() {
887 let mut model = TickIntervalModel::new();
888 model.record(5.0, 10, 0.2, 1000);
889 assert_eq!(model.training_sample_count(), 1);
890 }
891
892 #[test]
893 fn tick_interval_serde_roundtrip() {
894 let model = TickIntervalModel::new();
895 let json = serde_json::to_string(&model).unwrap();
896 let restored: TickIntervalModel = serde_json::from_str(&json).unwrap();
897 assert!(!restored.is_trained());
898 assert_eq!(restored.predict(0.0, 0, 0.0), 1000);
899 }
900
901 #[test]
904 fn complexity_fallback_500() {
905 let model = ComplexityModel::new();
906 assert!(!model.is_trained());
907
908 let threshold = model.predict(0, 200.0, 5.0);
909 assert_eq!(threshold, 500);
910 }
911
912 #[test]
913 fn complexity_record_increments_count() {
914 let mut model = ComplexityModel::new();
915 model.record(0, 200.0, 5.0, 600);
916 assert_eq!(model.training_sample_count(), 1);
917 }
918
919 #[test]
920 fn complexity_serde_roundtrip() {
921 let model = ComplexityModel::new();
922 let json = serde_json::to_string(&model).unwrap();
923 let restored: ComplexityModel = serde_json::from_str(&json).unwrap();
924 assert!(!restored.is_trained());
925 assert_eq!(restored.predict(0, 200.0, 5.0), 500);
926 }
927
928 #[test]
931 fn all_models_default_untrained() {
932 assert!(!GovernanceScorerModel::default().is_trained());
933 assert!(!RestartStrategyModel::default().is_trained());
934 assert!(!HealthThresholdModel::default().is_trained());
935 assert!(!DeadLetterModel::default().is_trained());
936 assert!(!GossipTimingModel::default().is_trained());
937 assert!(!ComplexityModel::default().is_trained());
938 }
939
940 #[test]
941 fn all_models_zero_initial_samples() {
942 assert_eq!(GovernanceScorerModel::new().training_sample_count(), 0);
943 assert_eq!(RestartStrategyModel::new().training_sample_count(), 0);
944 assert_eq!(HealthThresholdModel::new().training_sample_count(), 0);
945 assert_eq!(DeadLetterModel::new().training_sample_count(), 0);
946 assert_eq!(GossipTimingModel::new().training_sample_count(), 0);
947 assert_eq!(ComplexityModel::new().training_sample_count(), 0);
948 }
949}