Skip to main content

lean_ctx/core/
attribution.rs

1//! Deterministic identifiers and grouping for savings attribution records.
2
3/// Generate a stable, compact identifier for an attribution record.
4pub fn generate_attribution_id(
5    tool: &str,
6    mechanism: &str,
7    session_id: &str,
8    timestamp: &str,
9) -> String {
10    let identity = format!("{tool}|{mechanism}|{session_id}|{timestamp}");
11    let digest = blake3::hash(identity.as_bytes()).to_hex();
12    digest[..16].to_owned()
13}
14
15/// Return whether `new_id` has not already been booked.
16pub fn check_unique(existing_ids: &[&str], new_id: &str) -> bool {
17    !existing_ids.contains(&new_id)
18}
19
20/// Map a savings mechanism to its canonical attribution group.
21pub fn attribution_group_for_mechanism(mechanism: &str) -> &'static str {
22    match mechanism {
23        "compression" => "input_optimization",
24        "routing" => "model_selection",
25        "caching" => "cache_discount",
26        _ => "other",
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::{attribution_group_for_mechanism, check_unique, generate_attribution_id};
33
34    #[test]
35    fn generation_is_deterministic() {
36        let first = generate_attribution_id("tool", "compression", "session", "ts");
37        let second = generate_attribution_id("tool", "compression", "session", "ts");
38
39        assert_eq!(first, second);
40    }
41
42    #[test]
43    fn changing_identity_changes_id() {
44        let original = generate_attribution_id("tool", "compression", "session", "ts");
45        let changed = generate_attribution_id("other-tool", "compression", "session", "ts");
46
47        assert_ne!(original, changed);
48    }
49
50    #[test]
51    fn uniqueness_rejects_existing_id() {
52        assert!(check_unique(&["abc", "def"], "ghi"));
53        assert!(!check_unique(&["abc", "def"], "def"));
54    }
55
56    #[test]
57    fn mechanisms_map_to_expected_groups() {
58        assert_eq!(
59            attribution_group_for_mechanism("compression"),
60            "input_optimization"
61        );
62        assert_eq!(
63            attribution_group_for_mechanism("routing"),
64            "model_selection"
65        );
66        assert_eq!(attribution_group_for_mechanism("caching"), "cache_discount");
67        assert_eq!(attribution_group_for_mechanism("other"), "other");
68    }
69
70    #[test]
71    fn generated_id_is_a_16_character_hex_digest() {
72        let id = generate_attribution_id("tool", "routing", "session", "ts");
73
74        assert_eq!(id.len(), 16);
75        assert!(id.bytes().all(|byte| byte.is_ascii_hexdigit()));
76    }
77
78    #[test]
79    fn distinct_records_have_collision_resistant_ids() {
80        let first = generate_attribution_id("tool", "routing", "session-a", "ts");
81        let second = generate_attribution_id("tool", "routing", "session-b", "ts");
82        let third = generate_attribution_id("tool", "routing", "session-a", "ts+1");
83
84        assert_ne!(first, second);
85        assert_ne!(first, third);
86        assert_ne!(second, third);
87    }
88}