hematite/agent/
economics.rs1use serde::Serialize;
4
5#[derive(Debug, Clone)]
9pub struct ToolCost {
10 pub name: String,
11 pub tokens: usize,
13}
14
15#[derive(Debug, Clone)]
18pub struct TurnBudget {
19 pub input_tokens: usize,
21 pub output_tokens: usize,
23 pub history_est: usize,
25 pub tool_costs: Vec<ToolCost>,
27 pub context_pct: u8,
29}
30
31impl TurnBudget {
32 pub fn render(&self) -> String {
34 let total = self.input_tokens + self.output_tokens;
35 let mut parts = Vec::new();
36
37 if self.history_est > 0 {
38 parts.push(format!("prior hist ~{}t", self.history_est));
39 }
40 for tc in &self.tool_costs {
41 parts.push(format!("{} ~{}t", tc.name, tc.tokens));
42 }
43 if self.output_tokens > 0 {
44 parts.push(format!("model out {}t", self.output_tokens));
45 }
46
47 let breakdown = if parts.is_empty() {
48 String::new()
49 } else {
50 format!("\n {}", parts.join(" | "))
51 };
52
53 format!(
54 "Context budget: +{}t this turn ({}% ctx){}\n \
55 Tip: large tool results are the most common cause of context pressure.",
56 total, self.context_pct, breakdown
57 )
58 }
59}
60
61pub struct SessionEconomics {
63 pub input_tokens: usize,
65 pub output_tokens: usize,
67 pub tools_used: Vec<ToolRecord>,
69}
70
71impl SessionEconomics {
72 pub fn new() -> Self {
74 Self {
75 input_tokens: 0,
76 output_tokens: 0,
77 tools_used: Vec::new(),
78 }
79 }
80
81 pub fn record_tool(&mut self, name: &str, success: bool) {
83 self.tools_used.push(ToolRecord {
84 name: name.to_string(),
85 success,
86 });
87 }
88}
89
90impl Default for SessionEconomics {
91 fn default() -> Self {
92 Self {
93 input_tokens: 0,
94 output_tokens: 0,
95 tools_used: Vec::new(),
96 }
97 }
98}
99
100#[derive(Serialize, Clone, Debug)]
102pub struct ToolRecord {
103 pub name: String,
104 pub success: bool,
105}
106
107pub const INPUT_PRICE_PER_1K: f64 = 0.002;
111
112pub const OUTPUT_PRICE_PER_1K: f64 = 0.006;
114
115impl SessionEconomics {
118 pub fn simulated_cost(&self) -> f64 {
120 let input_cost = (self.input_tokens as f64 / 1000.0) * INPUT_PRICE_PER_1K;
121 let output_cost = (self.output_tokens as f64 / 1000.0) * OUTPUT_PRICE_PER_1K;
122 input_cost + output_cost
123 }
124
125 pub fn to_json(&self) -> String {
127 use serde_json::json;
128 json!({
129 "session_economics": {
130 "input_tokens": self.input_tokens,
131 "output_tokens": self.output_tokens,
132 "total_tokens": self.input_tokens + self.output_tokens,
133 "tools_used": self.tools_used.iter().map(|t| {
134 json!({
135 "name": t.name,
136 "success": t.success
137 })
138 }).collect::<Vec<_>>(),
139 "simulated_cost_usd": self.simulated_cost()
140 }
141 })
142 .to_string()
143 }
144}