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::with_capacity(self.tool_costs.len() + 2);
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
61#[derive(Default)]
63pub struct SessionEconomics {
64 pub input_tokens: usize,
66 pub output_tokens: usize,
68 pub tools_used: Vec<ToolRecord>,
70}
71
72impl SessionEconomics {
73 pub fn new() -> Self {
75 Self {
76 input_tokens: 0,
77 output_tokens: 0,
78 tools_used: Vec::new(),
79 }
80 }
81
82 pub fn record_tool(&mut self, name: &str, success: bool) {
84 self.tools_used.push(ToolRecord {
85 name: name.to_string(),
86 success,
87 });
88 }
89}
90
91#[derive(Serialize, Clone, Debug)]
93pub struct ToolRecord {
94 pub name: String,
95 pub success: bool,
96}
97
98pub const INPUT_PRICE_PER_1K: f64 = 0.002;
102
103pub const OUTPUT_PRICE_PER_1K: f64 = 0.006;
105
106impl SessionEconomics {
109 pub fn simulated_cost(&self) -> f64 {
111 let input_cost = (self.input_tokens as f64 / 1000.0) * INPUT_PRICE_PER_1K;
112 let output_cost = (self.output_tokens as f64 / 1000.0) * OUTPUT_PRICE_PER_1K;
113 input_cost + output_cost
114 }
115
116 pub fn to_json(&self) -> String {
118 use serde_json::json;
119 json!({
120 "session_economics": {
121 "input_tokens": self.input_tokens,
122 "output_tokens": self.output_tokens,
123 "total_tokens": self.input_tokens + self.output_tokens,
124 "tools_used": self.tools_used.iter().map(|t| {
125 json!({
126 "name": t.name,
127 "success": t.success
128 })
129 }).collect::<Vec<_>>(),
130 "simulated_cost_usd": self.simulated_cost()
131 }
132 })
133 .to_string()
134 }
135}