Skip to main content

orchestral_runtime/
interaction.rs

1//! Interaction - One intervention
2//!
3//! Interaction represents a single trigger → response attempt within a Thread.
4//! It replaces the concept of "Turn" with a more general abstraction.
5//!
6//! Key points:
7//! - Usually triggered by an Event
8//! - Multiple Interactions can exist in the same Thread concurrently
9
10use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12
13use super::thread::ThreadId;
14use orchestral_core::types::TaskId;
15
16pub use orchestral_core::store::InteractionId;
17
18/// Interaction state
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "snake_case")]
21pub enum InteractionState {
22    /// Interaction is active and processing
23    Active,
24    /// Interaction is waiting for user input
25    WaitingUser,
26    /// Interaction is waiting for external event
27    WaitingEvent,
28    /// Interaction is paused
29    Paused,
30    /// Interaction completed successfully
31    Completed,
32    /// Interaction failed
33    Failed,
34    /// Interaction was cancelled
35    Cancelled,
36}
37
38impl InteractionState {
39    /// Check if the interaction is in a terminal state
40    pub fn is_terminal(&self) -> bool {
41        matches!(
42            self,
43            InteractionState::Completed | InteractionState::Failed | InteractionState::Cancelled
44        )
45    }
46
47    /// Check if the interaction is active
48    pub fn is_active(&self) -> bool {
49        matches!(self, InteractionState::Active)
50    }
51
52    /// Check if the interaction is waiting
53    pub fn is_waiting(&self) -> bool {
54        matches!(
55            self,
56            InteractionState::WaitingUser | InteractionState::WaitingEvent
57        )
58    }
59}
60
61/// Interaction - a single trigger → response attempt
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct Interaction {
64    /// Unique identifier
65    pub id: InteractionId,
66    /// Thread this interaction belongs to
67    pub thread_id: ThreadId,
68    /// Current state
69    pub state: InteractionState,
70    /// Task IDs associated with this interaction
71    pub task_ids: Vec<TaskId>,
72    /// Start timestamp
73    pub started_at: DateTime<Utc>,
74    /// End timestamp (if completed)
75    pub ended_at: Option<DateTime<Utc>>,
76}
77
78impl Interaction {
79    /// Create a new interaction
80    pub fn new(thread_id: impl Into<ThreadId>) -> Self {
81        Self {
82            id: InteractionId::from(uuid::Uuid::new_v4().to_string()),
83            thread_id: thread_id.into(),
84            state: InteractionState::Active,
85            task_ids: Vec::new(),
86            started_at: Utc::now(),
87            ended_at: None,
88        }
89    }
90
91    /// Create a new interaction with specific ID
92    pub fn with_id(id: impl Into<InteractionId>, thread_id: impl Into<ThreadId>) -> Self {
93        Self {
94            id: id.into(),
95            thread_id: thread_id.into(),
96            state: InteractionState::Active,
97            task_ids: Vec::new(),
98            started_at: Utc::now(),
99            ended_at: None,
100        }
101    }
102
103    /// Add a task to this interaction
104    pub fn add_task(&mut self, task_id: TaskId) {
105        self.task_ids.push(task_id);
106    }
107
108    /// Set the interaction state
109    pub fn set_state(&mut self, state: InteractionState) {
110        let is_terminal = state.is_terminal();
111        self.state = state;
112        if is_terminal && self.ended_at.is_none() {
113            self.ended_at = Some(Utc::now());
114        }
115    }
116
117    /// Mark the interaction as completed
118    pub fn complete(&mut self) {
119        self.set_state(InteractionState::Completed);
120    }
121
122    /// Mark the interaction as failed
123    pub fn fail(&mut self) {
124        self.set_state(InteractionState::Failed);
125    }
126
127    /// Mark the interaction as cancelled
128    pub fn cancel(&mut self) {
129        self.set_state(InteractionState::Cancelled);
130    }
131
132    /// Mark the interaction as waiting for user
133    pub fn wait_for_user(&mut self) {
134        self.set_state(InteractionState::WaitingUser);
135    }
136
137    /// Mark the interaction as waiting for event
138    pub fn wait_for_event(&mut self) {
139        self.set_state(InteractionState::WaitingEvent);
140    }
141
142    /// Resume the interaction (set back to active)
143    pub fn resume(&mut self) {
144        if !self.state.is_terminal() {
145            self.state = InteractionState::Active;
146        }
147    }
148
149    /// Get the duration of this interaction (if ended)
150    pub fn duration(&self) -> Option<chrono::Duration> {
151        self.ended_at.map(|end| end - self.started_at)
152    }
153}