mempal 0.5.1

Project memory for coding agents. Single binary, hybrid search, knowledge graph.
Documentation
use std::collections::BTreeMap;

use serde::Serialize;

use crate::core::types::{RuntimeAdoptionEvent, RuntimeAdoptionSignal, RuntimeAdoptionTrack};

#[derive(Debug, Clone, Serialize)]
pub struct RuntimeAdoptionAnalyticsReport {
    pub writes: bool,
    pub total_events: usize,
    pub groups: Vec<RuntimeAdoptionAnalyticsGroup>,
}

#[derive(Debug, Clone, Serialize)]
pub struct RuntimeAdoptionAnalyticsGroup {
    pub track: String,
    pub feature: String,
    pub total: usize,
    pub used: usize,
    pub accepted: usize,
    pub rejected: usize,
    pub misses: usize,
    pub rollbacks: usize,
    pub contradictions: usize,
    pub neutral: usize,
    pub recommendation: String,
}

pub fn build_runtime_adoption_analytics(
    events: &[RuntimeAdoptionEvent],
) -> RuntimeAdoptionAnalyticsReport {
    let mut groups = BTreeMap::<(String, String), RuntimeAdoptionAnalyticsGroup>::new();
    for event in events {
        let track = runtime_adoption_track_slug(&event.track).to_string();
        let feature = event.feature.clone();
        let group = groups.entry((track.clone(), feature.clone())).or_insert(
            RuntimeAdoptionAnalyticsGroup {
                track,
                feature,
                total: 0,
                used: 0,
                accepted: 0,
                rejected: 0,
                misses: 0,
                rollbacks: 0,
                contradictions: 0,
                neutral: 0,
                recommendation: String::new(),
            },
        );
        group.total += 1;
        match event.signal {
            RuntimeAdoptionSignal::Used => group.used += 1,
            RuntimeAdoptionSignal::Accepted => group.accepted += 1,
            RuntimeAdoptionSignal::Rejected => group.rejected += 1,
            RuntimeAdoptionSignal::Miss => group.misses += 1,
            RuntimeAdoptionSignal::Rollback => group.rollbacks += 1,
            RuntimeAdoptionSignal::Contradiction => group.contradictions += 1,
            RuntimeAdoptionSignal::Neutral => group.neutral += 1,
        }
    }

    let mut groups = groups.into_values().collect::<Vec<_>>();
    for group in &mut groups {
        group.recommendation = recommendation_for(group).to_string();
    }

    RuntimeAdoptionAnalyticsReport {
        writes: false,
        total_events: events.len(),
        groups,
    }
}

fn recommendation_for(group: &RuntimeAdoptionAnalyticsGroup) -> &'static str {
    if group.rollbacks > 0 || group.contradictions > 0 {
        return "review_before_default_change";
    }
    if group.misses > group.accepted {
        return "investigate_misses";
    }
    if group.rejected > group.accepted {
        return "observe_or_rollback";
    }
    if group.accepted >= 3 && group.rejected <= group.accepted {
        return "candidate_for_workflow_hardening";
    }
    "observe_more_evidence"
}

fn runtime_adoption_track_slug(track: &RuntimeAdoptionTrack) -> &'static str {
    match track {
        RuntimeAdoptionTrack::RuntimeAdoption => "runtime_adoption",
        RuntimeAdoptionTrack::CardContext => "card_context",
        RuntimeAdoptionTrack::CardEmbedding => "card_embedding",
        RuntimeAdoptionTrack::Evaluator => "evaluator",
        RuntimeAdoptionTrack::ResearchAdapter => "research_adapter",
    }
}