Skip to main content

orchestral_runtime/
thread.rs

1//! Thread - Context world
2//!
3//! Thread represents a long-lived context where interactions take place.
4//! It replaces the concept of "Session" with a more general abstraction.
5//!
6//! Use cases:
7//! - Chat session
8//! - Ticket/Issue
9//! - IDE Workspace
10//! - Automation flow instance
11
12use chrono::{DateTime, Utc};
13use serde::{Deserialize, Serialize};
14use serde_json::Value;
15use std::collections::HashMap;
16
17pub use orchestral_core::store::ThreadId;
18
19fn default_scope() -> String {
20    "user:anonymous".to_string()
21}
22
23/// Thread - a long-lived context where interactions take place
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Thread {
26    /// Unique identifier
27    pub id: ThreadId,
28    /// Abstract ownership scope for access partitioning.
29    #[serde(default = "default_scope")]
30    pub scope: String,
31    /// Arbitrary metadata
32    #[serde(default)]
33    pub metadata: HashMap<String, Value>,
34    /// Creation timestamp
35    pub created_at: DateTime<Utc>,
36    /// Last activity timestamp
37    pub updated_at: DateTime<Utc>,
38}
39
40impl Thread {
41    /// Create a new thread
42    pub fn new() -> Self {
43        let now = Utc::now();
44        Self {
45            id: ThreadId::from(uuid::Uuid::new_v4().to_string()),
46            scope: default_scope(),
47            metadata: HashMap::new(),
48            created_at: now,
49            updated_at: now,
50        }
51    }
52
53    /// Create a new thread with specific ID
54    pub fn with_id(id: impl Into<ThreadId>) -> Self {
55        let now = Utc::now();
56        Self {
57            id: id.into(),
58            scope: default_scope(),
59            metadata: HashMap::new(),
60            created_at: now,
61            updated_at: now,
62        }
63    }
64
65    /// Create a new thread with metadata
66    pub fn with_metadata(metadata: HashMap<String, Value>) -> Self {
67        let now = Utc::now();
68        Self {
69            id: ThreadId::from(uuid::Uuid::new_v4().to_string()),
70            scope: default_scope(),
71            metadata,
72            created_at: now,
73            updated_at: now,
74        }
75    }
76
77    /// Create a new thread in a specific scope.
78    pub fn with_scope(scope: impl Into<String>) -> Self {
79        let now = Utc::now();
80        Self {
81            id: ThreadId::from(uuid::Uuid::new_v4().to_string()),
82            scope: scope.into(),
83            metadata: HashMap::new(),
84            created_at: now,
85            updated_at: now,
86        }
87    }
88
89    /// Set thread scope.
90    pub fn set_scope(&mut self, scope: impl Into<String>) {
91        self.scope = scope.into();
92        self.updated_at = Utc::now();
93    }
94
95    /// Set metadata value
96    pub fn set_metadata(&mut self, key: impl Into<String>, value: Value) {
97        self.metadata.insert(key.into(), value);
98        self.updated_at = Utc::now();
99    }
100
101    /// Get metadata value
102    pub fn get_metadata(&self, key: &str) -> Option<&Value> {
103        self.metadata.get(key)
104    }
105
106    /// Remove metadata value
107    pub fn remove_metadata(&mut self, key: &str) -> Option<Value> {
108        let result = self.metadata.remove(key);
109        if result.is_some() {
110            self.updated_at = Utc::now();
111        }
112        result
113    }
114
115    /// Touch the thread (update last activity timestamp)
116    pub fn touch(&mut self) {
117        self.updated_at = Utc::now();
118    }
119}
120
121impl Default for Thread {
122    fn default() -> Self {
123        Self::new()
124    }
125}