mempal_runtime/
adoption_analytics.rs1use std::collections::BTreeMap;
2
3use serde::Serialize;
4
5use crate::core::types::{RuntimeAdoptionEvent, RuntimeAdoptionSignal, RuntimeAdoptionTrack};
6
7#[derive(Debug, Clone, Serialize)]
8pub struct RuntimeAdoptionAnalyticsReport {
9 pub writes: bool,
10 pub total_events: usize,
11 pub groups: Vec<RuntimeAdoptionAnalyticsGroup>,
12}
13
14#[derive(Debug, Clone, Serialize)]
15pub struct RuntimeAdoptionAnalyticsGroup {
16 pub track: String,
17 pub feature: String,
18 pub total: usize,
19 pub used: usize,
20 pub accepted: usize,
21 pub rejected: usize,
22 pub misses: usize,
23 pub rollbacks: usize,
24 pub contradictions: usize,
25 pub neutral: usize,
26 pub recommendation: String,
27}
28
29pub fn build_runtime_adoption_analytics(
30 events: &[RuntimeAdoptionEvent],
31) -> RuntimeAdoptionAnalyticsReport {
32 let mut groups = BTreeMap::<(String, String), RuntimeAdoptionAnalyticsGroup>::new();
33 for event in events {
34 let track = runtime_adoption_track_slug(&event.track).to_string();
35 let feature = event.feature.clone();
36 let group = groups.entry((track.clone(), feature.clone())).or_insert(
37 RuntimeAdoptionAnalyticsGroup {
38 track,
39 feature,
40 total: 0,
41 used: 0,
42 accepted: 0,
43 rejected: 0,
44 misses: 0,
45 rollbacks: 0,
46 contradictions: 0,
47 neutral: 0,
48 recommendation: String::new(),
49 },
50 );
51 group.total += 1;
52 match event.signal {
53 RuntimeAdoptionSignal::Used => group.used += 1,
54 RuntimeAdoptionSignal::Accepted => group.accepted += 1,
55 RuntimeAdoptionSignal::Rejected => group.rejected += 1,
56 RuntimeAdoptionSignal::Miss => group.misses += 1,
57 RuntimeAdoptionSignal::Rollback => group.rollbacks += 1,
58 RuntimeAdoptionSignal::Contradiction => group.contradictions += 1,
59 RuntimeAdoptionSignal::Neutral => group.neutral += 1,
60 }
61 }
62
63 let mut groups = groups.into_values().collect::<Vec<_>>();
64 for group in &mut groups {
65 group.recommendation = recommendation_for(group).to_string();
66 }
67
68 RuntimeAdoptionAnalyticsReport {
69 writes: false,
70 total_events: events.len(),
71 groups,
72 }
73}
74
75fn recommendation_for(group: &RuntimeAdoptionAnalyticsGroup) -> &'static str {
76 if group.rollbacks > 0 || group.contradictions > 0 {
77 return "review_before_default_change";
78 }
79 if group.misses > group.accepted {
80 return "investigate_misses";
81 }
82 if group.rejected > group.accepted {
83 return "observe_or_rollback";
84 }
85 if group.accepted >= 3 && group.rejected <= group.accepted {
86 return "candidate_for_workflow_hardening";
87 }
88 "observe_more_evidence"
89}
90
91fn runtime_adoption_track_slug(track: &RuntimeAdoptionTrack) -> &'static str {
92 match track {
93 RuntimeAdoptionTrack::RuntimeAdoption => "runtime_adoption",
94 RuntimeAdoptionTrack::CardContext => "card_context",
95 RuntimeAdoptionTrack::CardEmbedding => "card_embedding",
96 RuntimeAdoptionTrack::Evaluator => "evaluator",
97 RuntimeAdoptionTrack::ResearchAdapter => "research_adapter",
98 }
99}