use std::sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc, Mutex,
};
use dashmap::DashMap;
#[derive(Debug)]
pub struct AwarenessState {
budget_remaining: AtomicUsize,
budget_set: AtomicBool,
pub target_model: Mutex<Option<String>>,
pub tokens_spent: AtomicUsize,
pub tool_calls_total: AtomicUsize,
pub expansion_calls: AtomicUsize,
pub bytes_compressed_in: AtomicUsize,
pub bytes_compressed_out: AtomicUsize,
cost_usd_micros: AtomicUsize,
uncached_equivalent_usd_micros: AtomicUsize,
uncached_input_tokens: AtomicUsize,
cache_write_tokens: AtomicUsize,
cache_read_tokens: AtomicUsize,
cost_output_tokens: AtomicUsize,
cost_estimated: AtomicBool,
prefix_diet_tokens_removed: AtomicUsize,
keepalive_pings_sent: AtomicUsize,
keepalive_estimated_usd_saved_micros: AtomicUsize,
digest_blocks: AtomicUsize,
digest_bytes_in: AtomicUsize,
digest_bytes_out: AtomicUsize,
quota_units_micros: AtomicUsize,
local_answered_turns: AtomicUsize,
routed_turns: AtomicUsize,
route_fallbacks: AtomicUsize,
routed_quota_saved_micros: AtomicUsize,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct CostSummary {
pub usd_total: f64,
pub usd_uncached_equivalent: f64,
pub uncached_input_tokens: u64,
pub cache_write_tokens: u64,
pub cache_read_tokens: u64,
pub output_tokens: u64,
pub estimated: bool,
pub prefix_diet_tokens_removed: u64,
pub keepalive_pings_sent: u64,
pub keepalive_estimated_usd_saved: f64,
pub digest_blocks: u64,
pub digest_bytes_in: u64,
pub digest_bytes_out: u64,
pub quota_units_total: f64,
pub local_answered_turns: u64,
pub routed_turns: u64,
pub route_fallbacks: u64,
pub routed_quota_saved_units: f64,
}
impl CostSummary {
pub fn cache_hit_rate(&self) -> f64 {
let denom = self.cache_read_tokens + self.cache_write_tokens + self.uncached_input_tokens;
if denom == 0 {
0.0
} else {
self.cache_read_tokens as f64 / denom as f64
}
}
}
impl Default for AwarenessState {
fn default() -> Self {
Self {
budget_remaining: AtomicUsize::new(0),
budget_set: AtomicBool::new(false),
target_model: Mutex::new(None),
tokens_spent: AtomicUsize::new(0),
tool_calls_total: AtomicUsize::new(0),
expansion_calls: AtomicUsize::new(0),
bytes_compressed_in: AtomicUsize::new(0),
bytes_compressed_out: AtomicUsize::new(0),
cost_usd_micros: AtomicUsize::new(0),
uncached_equivalent_usd_micros: AtomicUsize::new(0),
uncached_input_tokens: AtomicUsize::new(0),
cache_write_tokens: AtomicUsize::new(0),
cache_read_tokens: AtomicUsize::new(0),
cost_output_tokens: AtomicUsize::new(0),
cost_estimated: AtomicBool::new(false),
prefix_diet_tokens_removed: AtomicUsize::new(0),
keepalive_pings_sent: AtomicUsize::new(0),
keepalive_estimated_usd_saved_micros: AtomicUsize::new(0),
digest_blocks: AtomicUsize::new(0),
digest_bytes_in: AtomicUsize::new(0),
digest_bytes_out: AtomicUsize::new(0),
quota_units_micros: AtomicUsize::new(0),
local_answered_turns: AtomicUsize::new(0),
routed_turns: AtomicUsize::new(0),
route_fallbacks: AtomicUsize::new(0),
routed_quota_saved_micros: AtomicUsize::new(0),
}
}
}
impl AwarenessState {
pub fn set_budget(&self, remaining: usize, model: Option<String>) {
self.budget_remaining.store(remaining, Ordering::Relaxed);
self.budget_set.store(true, Ordering::Relaxed);
if let Some(m) = model {
if let Ok(mut g) = self.target_model.lock() {
*g = Some(m);
}
}
}
pub fn record_tool_response(&self, tool: &str, token_cost: usize) {
self.tokens_spent.fetch_add(token_cost, Ordering::Relaxed);
self.tool_calls_total.fetch_add(1, Ordering::Relaxed);
if tool == "axiom_expand" {
self.expansion_calls.fetch_add(1, Ordering::Relaxed);
}
}
pub fn record_compression(&self, bytes_in: usize, bytes_out: usize) {
self.bytes_compressed_in
.fetch_add(bytes_in, Ordering::Relaxed);
self.bytes_compressed_out
.fetch_add(bytes_out, Ordering::Relaxed);
}
pub fn record_turn_cost(
&self,
tc: &crate::cost_ledger::TurnCost,
prices: &crate::cost_ledger::PriceTable,
) {
let micros = (tc.usd * 1_000_000.0).round() as usize;
self.cost_usd_micros.fetch_add(micros, Ordering::Relaxed);
let uncached_micros = (tc.uncached_equivalent_usd(prices) * 1_000_000.0).round() as usize;
self.uncached_equivalent_usd_micros
.fetch_add(uncached_micros, Ordering::Relaxed);
self.uncached_input_tokens
.fetch_add(tc.uncached_in as usize, Ordering::Relaxed);
self.cache_write_tokens
.fetch_add(tc.cache_write as usize, Ordering::Relaxed);
self.cache_read_tokens
.fetch_add(tc.cache_read as usize, Ordering::Relaxed);
self.cost_output_tokens
.fetch_add(tc.output as usize, Ordering::Relaxed);
if tc.estimated {
self.cost_estimated.store(true, Ordering::Relaxed);
}
}
pub fn cost_summary(&self) -> CostSummary {
CostSummary {
usd_total: self.cost_usd_micros.load(Ordering::Relaxed) as f64 / 1_000_000.0,
usd_uncached_equivalent: self.uncached_equivalent_usd_micros.load(Ordering::Relaxed)
as f64
/ 1_000_000.0,
uncached_input_tokens: self.uncached_input_tokens.load(Ordering::Relaxed) as u64,
cache_write_tokens: self.cache_write_tokens.load(Ordering::Relaxed) as u64,
cache_read_tokens: self.cache_read_tokens.load(Ordering::Relaxed) as u64,
output_tokens: self.cost_output_tokens.load(Ordering::Relaxed) as u64,
estimated: self.cost_estimated.load(Ordering::Relaxed),
prefix_diet_tokens_removed: self.prefix_diet_tokens_removed.load(Ordering::Relaxed)
as u64,
keepalive_pings_sent: self.keepalive_pings_sent.load(Ordering::Relaxed) as u64,
keepalive_estimated_usd_saved: self
.keepalive_estimated_usd_saved_micros
.load(Ordering::Relaxed) as f64
/ 1_000_000.0,
digest_blocks: self.digest_blocks.load(Ordering::Relaxed) as u64,
digest_bytes_in: self.digest_bytes_in.load(Ordering::Relaxed) as u64,
digest_bytes_out: self.digest_bytes_out.load(Ordering::Relaxed) as u64,
quota_units_total: self.quota_units_micros.load(Ordering::Relaxed) as f64
/ 1_000_000.0,
local_answered_turns: self.local_answered_turns.load(Ordering::Relaxed) as u64,
routed_turns: self.routed_turns.load(Ordering::Relaxed) as u64,
route_fallbacks: self.route_fallbacks.load(Ordering::Relaxed) as u64,
routed_quota_saved_units: self.routed_quota_saved_micros.load(Ordering::Relaxed) as f64
/ 1_000_000.0,
}
}
pub fn record_route(&self, units_saved: f64) {
self.routed_turns.fetch_add(1, Ordering::Relaxed);
let micros = (units_saved.max(0.0) * 1_000_000.0).round() as usize;
self.routed_quota_saved_micros
.fetch_add(micros, Ordering::Relaxed);
}
pub fn record_route_fallback(&self) {
self.route_fallbacks.fetch_add(1, Ordering::Relaxed);
}
pub fn record_turn_quota(&self, units: f64) {
let micros = (units.max(0.0) * 1_000_000.0).round() as usize;
self.quota_units_micros
.fetch_add(micros, Ordering::Relaxed);
}
pub fn record_local_answer(&self) {
self.local_answered_turns.fetch_add(1, Ordering::Relaxed);
}
pub fn record_prefix_diet(&self, tokens_removed: usize) {
self.prefix_diet_tokens_removed
.fetch_add(tokens_removed, Ordering::Relaxed);
}
pub fn record_keepalive_ping(&self, estimated_usd_saved: f64) {
self.keepalive_pings_sent.fetch_add(1, Ordering::Relaxed);
let micros = (estimated_usd_saved.max(0.0) * 1_000_000.0).round() as usize;
self.keepalive_estimated_usd_saved_micros
.fetch_add(micros, Ordering::Relaxed);
}
pub fn record_digest(&self, blocks: usize, bytes_in: usize, bytes_out: usize) {
self.digest_blocks.fetch_add(blocks, Ordering::Relaxed);
self.digest_bytes_in.fetch_add(bytes_in, Ordering::Relaxed);
self.digest_bytes_out
.fetch_add(bytes_out, Ordering::Relaxed);
}
pub fn budget(&self) -> Option<usize> {
if self.budget_set.load(Ordering::Relaxed) {
Some(self.budget_remaining.load(Ordering::Relaxed))
} else {
None
}
}
pub fn compression_target_tokens(&self) -> Option<usize> {
self.budget()
.map(|b| ((b as f64 * 0.6) as usize).max(512))
}
pub fn compression_ratio(&self) -> Option<f32> {
let bytes_in = self.bytes_compressed_in.load(Ordering::Relaxed);
if bytes_in == 0 {
None
} else {
Some(
self.bytes_compressed_out.load(Ordering::Relaxed) as f32
/ bytes_in as f32,
)
}
}
pub fn is_tight(&self) -> bool {
self.budget().map(|b| b < 20_000).unwrap_or(false)
}
pub fn recommendation(&self) -> Option<String> {
let budget = self.budget()?;
let spent = self.tokens_spent.load(Ordering::Relaxed);
let pct = (spent * 100).checked_div(budget + spent).unwrap_or(0);
let expansions = self.expansion_calls.load(Ordering::Relaxed);
let mut msgs = Vec::new();
if budget < 20_000 {
msgs.push(
"Budget < 20 k — Axiom is in compact-response mode.".to_string(),
);
} else if budget < 50_000 {
msgs.push(format!(
"Budget at {} k remaining ({pct}% spent on Axiom responses). Consider pre-compressing large paths.",
budget / 1_000
));
}
if expansions > 2 {
msgs.push(format!(
"{expansions} symbol expansions — compression may be too aggressive; raise AXIOM_TTT_COMPRESS_THRESHOLD_TOKENS."
));
}
if msgs.is_empty() {
None
} else {
Some(msgs.join(" "))
}
}
}
#[derive(Clone, Default)]
pub struct AwarenessStore {
inner: Arc<DashMap<String, Arc<AwarenessState>>>,
}
impl AwarenessStore {
pub fn new() -> Self {
Self {
inner: Arc::new(DashMap::new()),
}
}
pub fn get_or_create(&self, id: &str) -> Arc<AwarenessState> {
self.inner
.entry(id.to_string())
.or_insert_with(|| Arc::new(AwarenessState::default()))
.clone()
}
pub fn get(&self, id: &str) -> Option<Arc<AwarenessState>> {
self.inner.get(id).map(|r| r.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_turn_cost_accumulates_usd_and_uncached_equivalent() {
let s = AwarenessState::default();
let tc1 = crate::cost_ledger::TurnCost {
uncached_in: 1000,
cache_write: 0,
cache_read: 0,
output: 500,
usd: 0.0105,
estimated: false,
};
let tc2 = crate::cost_ledger::TurnCost {
uncached_in: 0,
cache_write: 0,
cache_read: 80_000,
output: 300,
usd: 0.0285,
estimated: false,
};
let (prices, _) = crate::cost_ledger::PriceTable::for_model("claude-sonnet-4-6");
s.record_turn_cost(&tc1, &prices);
s.record_turn_cost(&tc2, &prices);
let summary = s.cost_summary();
assert!((summary.usd_total - (0.0105 + 0.0285)).abs() < 1e-9);
assert_eq!(summary.cache_read_tokens, 80_000);
assert_eq!(summary.uncached_input_tokens, 1000);
assert_eq!(summary.output_tokens, 800);
assert!(!summary.estimated, "no turn was estimated");
}
#[test]
fn record_turn_cost_marks_summary_estimated_if_any_turn_was() {
let s = AwarenessState::default();
let (prices, _) = crate::cost_ledger::PriceTable::for_model("claude-sonnet-4-6");
s.record_turn_cost(
&crate::cost_ledger::TurnCost {
estimated: true,
..Default::default()
},
&prices,
);
assert!(s.cost_summary().estimated);
}
#[test]
fn cache_hit_rate_is_reads_over_reads_plus_writes_plus_uncached() {
let s = AwarenessState::default();
let (prices, _) = crate::cost_ledger::PriceTable::for_model("claude-sonnet-4-6");
s.record_turn_cost(
&crate::cost_ledger::TurnCost {
uncached_in: 100,
cache_write: 100,
cache_read: 800,
..Default::default()
},
&prices,
);
let rate = s.cost_summary().cache_hit_rate();
assert!((rate - 0.8).abs() < 1e-9);
}
#[test]
fn cost_summary_uncached_equivalent_exceeds_actual_and_accumulates() {
let s = AwarenessState::default();
let (prices, _) = crate::cost_ledger::PriceTable::for_model("claude-sonnet-4-6");
let tc = crate::cost_ledger::TurnCost {
uncached_in: 1000,
cache_write: 2000,
cache_read: 77_000,
output: 500,
usd: 0.0, estimated: false,
};
s.record_turn_cost(&tc, &prices);
let summary = s.cost_summary();
let expected_uncached = tc.uncached_equivalent_usd(&prices);
assert!(
(summary.usd_uncached_equivalent - expected_uncached).abs() < 1e-6,
"{} vs {}",
summary.usd_uncached_equivalent,
expected_uncached
);
assert!(summary.usd_uncached_equivalent > 0.0);
}
#[test]
fn budget_set_and_read() {
let s = AwarenessState::default();
assert!(s.budget().is_none());
s.set_budget(40_000, Some("claude-sonnet-4-6".into()));
assert_eq!(s.budget(), Some(40_000));
assert_eq!(
s.target_model.lock().unwrap().as_deref(),
Some("claude-sonnet-4-6")
);
}
#[test]
fn compression_target_is_sixty_pct() {
let s = AwarenessState::default();
s.set_budget(50_000, None);
assert_eq!(s.compression_target_tokens(), Some(30_000));
}
#[test]
fn compression_target_floors_at_512() {
let s = AwarenessState::default();
s.set_budget(100, None);
assert_eq!(s.compression_target_tokens(), Some(512));
}
#[test]
fn is_tight_below_20k() {
let s = AwarenessState::default();
s.set_budget(15_000, None);
assert!(s.is_tight());
s.set_budget(25_000, None);
assert!(!s.is_tight());
}
#[test]
fn compression_ratio_none_until_data() {
let s = AwarenessState::default();
assert!(s.compression_ratio().is_none());
s.record_compression(1000, 400);
let ratio = s.compression_ratio().unwrap();
assert!((ratio - 0.4).abs() < 1e-5);
}
#[test]
fn token_spend_accumulates() {
let s = AwarenessState::default();
s.record_tool_response("axiom_recall", 50);
s.record_tool_response("axiom_expand", 30);
assert_eq!(s.tokens_spent.load(Ordering::Relaxed), 80);
assert_eq!(s.expansion_calls.load(Ordering::Relaxed), 1);
assert_eq!(s.tool_calls_total.load(Ordering::Relaxed), 2);
}
#[test]
fn store_get_or_create_is_idempotent() {
let store = AwarenessStore::new();
let a = store.get_or_create("sess-1");
let b = store.get_or_create("sess-1");
a.set_budget(10_000, None);
assert_eq!(b.budget(), Some(10_000));
}
}