Skip to main content

mempal_runtime/
adoption_analytics.rs

1use 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}