agent_engine/engine/
session.rs1use crate::pricing::calculate_cost_optional_split;
7use crate::SharedMessage;
8use crate::{Runtime, Session};
9
10pub struct ConversationState {
12 pub session: Session,
13 pub api_messages: Vec<SharedMessage>,
14 pub total_input_tokens: u64,
15 pub total_output_tokens: u64,
16 pub total_cache_read_tokens: u64,
17 pub total_cache_creation_tokens: u64,
18 pub session_cost: f64,
19 pub abort_context: Option<String>,
20 pub queued_message: Option<String>,
22 pub pending_events: Vec<String>,
24}
25
26impl ConversationState {
27 pub fn new(session: Session) -> Self {
29 Self {
30 session,
31 api_messages: Vec::new(),
32 total_input_tokens: 0,
33 total_output_tokens: 0,
34 total_cache_read_tokens: 0,
35 total_cache_creation_tokens: 0,
36 session_cost: 0.0,
37 abort_context: None,
38 queued_message: None,
39 pending_events: Vec::new(),
40 }
41 }
42
43 pub fn from_resumed(session: Session) -> Self {
45 Self {
46 api_messages: session.api_messages.clone(),
47 total_input_tokens: session.total_input_tokens,
48 total_output_tokens: session.total_output_tokens,
49 total_cache_read_tokens: 0,
50 total_cache_creation_tokens: 0,
51 session_cost: session.session_cost,
52 abort_context: session.abort_context.clone(),
53 queued_message: None,
54 pending_events: Vec::new(),
55 session,
56 }
57 }
58
59 pub async fn save(&mut self) {
61 if self.api_messages.is_empty() {
62 return;
63 }
64 self.session.api_messages = self.api_messages.clone();
65 self.session.total_input_tokens = self.total_input_tokens;
66 self.session.total_output_tokens = self.total_output_tokens;
67 self.session.session_cost = self.session_cost;
68 self.session.abort_context = self.abort_context.clone();
69 self.session.updated_at = chrono::Utc::now();
70 self.session.auto_title();
71 if let Err(e) = self.session.save().await {
72 tracing::error!("Failed to save session: {}", e);
73 }
74 }
75
76 pub async fn clear(&mut self, runtime: &Runtime) {
78 self.save().await;
79 self.api_messages.clear();
80 self.total_input_tokens = 0;
81 self.total_output_tokens = 0;
82 self.total_cache_read_tokens = 0;
83 self.total_cache_creation_tokens = 0;
84 self.session_cost = 0.0;
85 self.abort_context = None;
86 self.queued_message = None;
87 self.pending_events.clear();
88 self.session = Session::new(
89 runtime.model(),
90 runtime.thinking_level(),
91 runtime.system_prompt(),
92 );
93 }
94
95 #[allow(clippy::too_many_arguments)]
102 pub fn add_usage(
103 &mut self,
104 input_tokens: u64,
105 output_tokens: u64,
106 cache_read: u64,
107 cache_creation: u64,
108 cache_creation_5m: Option<u64>,
109 cache_creation_1h: Option<u64>,
110 model: &str,
111 ) {
112 self.total_input_tokens += input_tokens;
113 self.total_output_tokens += output_tokens;
114 self.total_cache_read_tokens += cache_read;
115 self.total_cache_creation_tokens += cache_creation;
116
117 self.session_cost += calculate_cost_optional_split(
118 model,
119 input_tokens,
120 output_tokens,
121 cache_read,
122 cache_creation,
123 cache_creation_5m,
124 cache_creation_1h,
125 );
126 }
127}