starweaver_context/
run_state.rs1use chrono::Utc;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use starweaver_core::{ConversationId, Metadata, RunId, RunLifecycle as RunStatus, TaskId};
7use starweaver_model::{ModelMessage, ModelResponse, ToolCallPart, ToolReturnPart};
8use starweaver_usage::Usage;
9
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
12pub struct AgentRunState {
13 pub run_id: RunId,
15 pub conversation_id: ConversationId,
17 pub message_history: Vec<ModelMessage>,
19 pub usage: Usage,
21 pub run_step: usize,
23 pub status: RunStatus,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub output: Option<String>,
28 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub structured_output: Option<Value>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub latest_response: Option<ModelResponse>,
34 #[serde(default, skip_serializing_if = "Vec::is_empty")]
36 pub pending_tool_calls: Vec<ToolCallPart>,
37 #[serde(default, skip_serializing_if = "Vec::is_empty")]
39 pub pending_tool_returns: Vec<ToolReturnPart>,
40 #[serde(default, skip_serializing_if = "Vec::is_empty")]
42 pub pending_approval_tool_returns: Vec<ToolReturnPart>,
43 #[serde(default, skip_serializing_if = "Vec::is_empty")]
45 pub deferred_tool_returns: Vec<ToolReturnPart>,
46 #[serde(default, skip_serializing_if = "Vec::is_empty")]
48 pub idle_messages: Vec<String>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub parent_run_id: Option<RunId>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub parent_task_id: Option<TaskId>,
55 #[serde(default, skip_serializing_if = "Metadata::is_empty")]
57 pub metadata: Metadata,
58}
59
60impl AgentRunState {
61 #[must_use]
63 pub fn new(run_id: RunId, conversation_id: ConversationId) -> Self {
64 Self {
65 run_id,
66 conversation_id,
67 message_history: Vec::new(),
68 usage: Usage::default(),
69 run_step: 0,
70 status: RunStatus::Starting,
71 output: None,
72 structured_output: None,
73 latest_response: None,
74 pending_tool_calls: Vec::new(),
75 pending_tool_returns: Vec::new(),
76 pending_approval_tool_returns: Vec::new(),
77 deferred_tool_returns: Vec::new(),
78 idle_messages: Vec::new(),
79 parent_run_id: None,
80 parent_task_id: None,
81 metadata: Metadata::default(),
82 }
83 }
84
85 pub fn apply_model_response(&mut self, mut response: ModelResponse) {
87 response.run_id.get_or_insert_with(|| self.run_id.clone());
88 response
89 .conversation_id
90 .get_or_insert_with(|| self.conversation_id.clone());
91 response.timestamp.get_or_insert_with(Utc::now);
92 self.usage.add_assign(&response.usage);
93 self.message_history
94 .push(ModelMessage::Response(response.clone()));
95 self.latest_response = Some(response);
96 }
97
98 pub fn replace_latest_response(&mut self, response: ModelResponse) {
100 if let Some(ModelMessage::Response(history_response)) = self.message_history.last_mut() {
101 *history_response = response.clone();
102 }
103 self.latest_response = Some(response);
104 }
105
106 #[must_use]
108 pub const fn has_pending_hitl(&self) -> bool {
109 !self.pending_approval_tool_returns.is_empty() || !self.deferred_tool_returns.is_empty()
110 }
111
112 #[must_use]
114 pub fn pending_approvals(&self) -> &[ToolReturnPart] {
115 &self.pending_approval_tool_returns
116 }
117
118 #[must_use]
120 pub fn pending_deferred_tools(&self) -> &[ToolReturnPart] {
121 &self.deferred_tool_returns
122 }
123
124 pub fn pending_hitl_tool_returns(&self) -> impl Iterator<Item = &ToolReturnPart> {
126 self.pending_approval_tool_returns
127 .iter()
128 .chain(self.deferred_tool_returns.iter())
129 }
130}