Skip to main content

engram/context/
metrics.rs

1//! Conservative Operational Context efficiency metrics.
2//!
3//! These helpers report estimates only. They are intended to make storage,
4//! retrieval, and bundle construction auditable without claiming first-pass
5//! shell-output compression or guaranteed savings.
6
7use serde::{Deserialize, Serialize};
8use serde_json::{json, Value};
9
10const CHARS_PER_TOKEN_ESTIMATE: usize = 4;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct TokenEstimateMetadata {
14    pub tokens_est: i64,
15    pub bytes: usize,
16    pub method: String,
17}
18
19#[derive(Debug, Clone, Default, Serialize, Deserialize)]
20pub struct ContextBundleMetrics {
21    pub metric_type: String,
22    pub estimated: bool,
23    pub method: String,
24    pub retrieved_item_count: usize,
25    pub included_section_entry_count: usize,
26    pub excluded_item_count: usize,
27    pub artifact_pointer_count: usize,
28    pub summarized_artifact_ref_count: usize,
29    pub raw_artifact_ref_count: usize,
30    pub raw_artifact_return_count: usize,
31    pub retrieved_context_tokens_est: i64,
32    pub bundle_tokens_est: i64,
33    pub excluded_tokens_est: Option<i64>,
34    pub notes: Vec<String>,
35}
36
37#[derive(Debug, Clone, Default, Serialize, Deserialize)]
38pub struct ArtifactRetrievalCounters {
39    pub summarized_artifact_refs: usize,
40    pub raw_artifact_refs: usize,
41    pub raw_artifacts_returned: usize,
42}
43
44pub fn estimate_tokens(text: &str) -> i64 {
45    if text.is_empty() {
46        return 0;
47    }
48    text.len().div_ceil(CHARS_PER_TOKEN_ESTIMATE) as i64
49}
50
51pub fn estimate_json_tokens(value: &Value) -> i64 {
52    estimate_tokens(&value.to_string())
53}
54
55pub fn token_estimate_metadata(text: &str) -> TokenEstimateMetadata {
56    TokenEstimateMetadata {
57        tokens_est: estimate_tokens(text),
58        bytes: text.len(),
59        method: "chars_div_4_estimate".to_string(),
60    }
61}
62
63pub fn estimated_savings_tokens(
64    raw_tokens: Option<i64>,
65    compact_tokens: Option<i64>,
66) -> Option<i64> {
67    match (raw_tokens, compact_tokens) {
68        (Some(raw), Some(compact)) if raw >= compact => Some(raw - compact),
69        _ => None,
70    }
71}
72
73pub fn metrics_value(
74    observed_input_tokens_est: Option<i64>,
75    stored_artifact_tokens_est: Option<i64>,
76    summary_tokens_est: Option<i64>,
77) -> Value {
78    json!({
79        "estimated": true,
80        "method": "chars_div_4_estimate_or_caller_supplied",
81        "observed_input_tokens_est": observed_input_tokens_est,
82        "stored_artifact_tokens_est": stored_artifact_tokens_est,
83        "summary_tokens_est": summary_tokens_est,
84        "estimated_savings_tokens": estimated_savings_tokens(
85            observed_input_tokens_est,
86            summary_tokens_est.or(stored_artifact_tokens_est),
87        ),
88        "claim": "cross_session_retrieval_estimate_only"
89    })
90}