tirea_contract/runtime/phase/
action_set.rs1use crate::runtime::action::Action;
2use crate::runtime::inference::InferenceRequestTransform;
3use crate::runtime::run::TerminationReason;
4use crate::runtime::state::AnyStateAction;
5use crate::runtime::tool_call::gate::{SuspendTicket, ToolCallAction};
6use crate::runtime::tool_call::ToolResult;
7use std::sync::Arc;
8
9#[derive(Default)]
21pub struct ActionSet<A>(Vec<A>);
22
23impl<A> ActionSet<A> {
24 pub fn empty() -> Self {
26 Self(Vec::new())
27 }
28
29 pub fn single(a: impl Into<A>) -> Self {
31 Self(vec![a.into()])
32 }
33
34 #[must_use]
36 pub fn and(mut self, other: impl Into<ActionSet<A>>) -> Self {
37 self.0.extend(other.into().0);
38 self
39 }
40
41 pub fn is_empty(&self) -> bool {
42 self.0.is_empty()
43 }
44
45 pub fn len(&self) -> usize {
46 self.0.len()
47 }
48
49 pub fn as_slice(&self) -> &[A] {
51 &self.0
52 }
53
54 pub fn into_vec(self) -> Vec<A> {
56 self.0
57 }
58}
59
60impl<A> IntoIterator for ActionSet<A> {
61 type Item = A;
62 type IntoIter = std::vec::IntoIter<A>;
63 fn into_iter(self) -> Self::IntoIter {
64 self.0.into_iter()
65 }
66}
67
68impl<A> From<Vec<A>> for ActionSet<A> {
69 fn from(v: Vec<A>) -> Self {
70 Self(v)
71 }
72}
73
74impl<A> Extend<A> for ActionSet<A> {
75 fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
76 self.0.extend(iter);
77 }
78}
79
80pub enum LifecycleAction {
88 State(AnyStateAction),
89}
90
91impl From<AnyStateAction> for LifecycleAction {
92 fn from(sa: AnyStateAction) -> Self {
93 Self::State(sa)
94 }
95}
96
97impl From<LifecycleAction> for ActionSet<LifecycleAction> {
98 fn from(a: LifecycleAction) -> Self {
99 ActionSet::single(a)
100 }
101}
102
103impl From<AnyStateAction> for ActionSet<LifecycleAction> {
104 fn from(sa: AnyStateAction) -> Self {
105 ActionSet::single(LifecycleAction::State(sa))
106 }
107}
108
109pub enum BeforeInferenceAction {
113 AddSystemContext(String),
115 AddSessionContext(String),
117 ExcludeTool(String),
119 IncludeOnlyTools(Vec<String>),
121 AddRequestTransform(Arc<dyn InferenceRequestTransform>),
123 Terminate(TerminationReason),
125 State(AnyStateAction),
127}
128
129impl From<AnyStateAction> for BeforeInferenceAction {
130 fn from(sa: AnyStateAction) -> Self {
131 Self::State(sa)
132 }
133}
134
135impl From<BeforeInferenceAction> for ActionSet<BeforeInferenceAction> {
136 fn from(a: BeforeInferenceAction) -> Self {
137 ActionSet::single(a)
138 }
139}
140
141impl From<AnyStateAction> for ActionSet<BeforeInferenceAction> {
142 fn from(sa: AnyStateAction) -> Self {
143 ActionSet::single(BeforeInferenceAction::State(sa))
144 }
145}
146
147pub enum AfterInferenceAction {
151 Terminate(TerminationReason),
153 State(AnyStateAction),
155}
156
157impl From<AnyStateAction> for AfterInferenceAction {
158 fn from(sa: AnyStateAction) -> Self {
159 Self::State(sa)
160 }
161}
162
163impl From<AfterInferenceAction> for ActionSet<AfterInferenceAction> {
164 fn from(a: AfterInferenceAction) -> Self {
165 ActionSet::single(a)
166 }
167}
168
169impl From<AnyStateAction> for ActionSet<AfterInferenceAction> {
170 fn from(sa: AnyStateAction) -> Self {
171 ActionSet::single(AfterInferenceAction::State(sa))
172 }
173}
174
175pub enum BeforeToolExecuteAction {
179 Block(String),
181 Suspend(SuspendTicket),
183 SetToolResult(ToolResult),
185 State(AnyStateAction),
187}
188
189impl BeforeToolExecuteAction {
190 pub fn from_decision(decision: ToolCallAction) -> Self {
192 match decision {
193 ToolCallAction::Block { reason } => Self::Block(reason),
194 ToolCallAction::Suspend(ticket) => Self::Suspend(*ticket),
195 ToolCallAction::Proceed => {
196 unreachable!("Proceed is not emitted as a BeforeToolExecuteAction")
197 }
198 }
199 }
200}
201
202impl From<AnyStateAction> for BeforeToolExecuteAction {
203 fn from(sa: AnyStateAction) -> Self {
204 Self::State(sa)
205 }
206}
207
208impl From<BeforeToolExecuteAction> for ActionSet<BeforeToolExecuteAction> {
209 fn from(a: BeforeToolExecuteAction) -> Self {
210 ActionSet::single(a)
211 }
212}
213
214impl From<AnyStateAction> for ActionSet<BeforeToolExecuteAction> {
215 fn from(sa: AnyStateAction) -> Self {
216 ActionSet::single(BeforeToolExecuteAction::State(sa))
217 }
218}
219
220pub enum AfterToolExecuteAction {
224 AddSystemReminder(String),
226 AddUserMessage(String),
228 State(AnyStateAction),
230}
231
232impl Action for AfterToolExecuteAction {
233 fn label(&self) -> &'static str {
234 match self {
235 Self::AddSystemReminder(_) => "add_system_reminder",
236 Self::AddUserMessage(_) => "add_user_message",
237 Self::State(_) => "state_action",
238 }
239 }
240
241 fn validate(&self, phase: super::types::Phase) -> Result<(), String> {
242 if phase == super::types::Phase::AfterToolExecute {
243 Ok(())
244 } else {
245 Err(format!(
246 "AfterToolExecuteAction '{}' is only valid in AfterToolExecute, got {phase}",
247 self.label()
248 ))
249 }
250 }
251
252 fn apply(self: Box<Self>, step: &mut super::step::StepContext<'_>) {
253 match *self {
254 Self::AddSystemReminder(text) => step.messaging.reminders.push(text),
255 Self::AddUserMessage(text) => step.messaging.user_messages.push(text),
256 Self::State(action) => step.emit_state_action(action),
257 }
258 }
259
260 fn is_state_action(&self) -> bool {
261 matches!(self, Self::State(_))
262 }
263
264 fn into_state_action(self: Box<Self>) -> Option<AnyStateAction> {
265 match *self {
266 Self::State(action) => Some(action),
267 _ => None,
268 }
269 }
270}
271
272impl From<AnyStateAction> for AfterToolExecuteAction {
273 fn from(sa: AnyStateAction) -> Self {
274 Self::State(sa)
275 }
276}
277
278impl From<AfterToolExecuteAction> for ActionSet<AfterToolExecuteAction> {
279 fn from(a: AfterToolExecuteAction) -> Self {
280 ActionSet::single(a)
281 }
282}
283
284impl From<AnyStateAction> for ActionSet<AfterToolExecuteAction> {
285 fn from(sa: AnyStateAction) -> Self {
286 ActionSet::single(AfterToolExecuteAction::State(sa))
287 }
288}