Skip to main content

graph_flow/
context.rs

1//! Context and state management for workflows.
2//!
3//! This module provides thread-safe state management across workflow tasks,
4//! including regular data storage and specialized chat history management.
5//!
6//! # Examples
7//!
8//! ## Basic Context Usage
9//!
10//! ```rust
11//! use graph_flow::Context;
12//!
13//! # #[tokio::main]
14//! # async fn main() {
15//! let context = Context::new();
16//!
17//! // Store different types of data
18//! context.set("user_id", 12345).await;
19//! context.set("name", "Alice".to_string()).await;
20//! context.set("active", true).await;
21//!
22//! // Retrieve data with type safety
23//! let user_id: Option<i32> = context.get("user_id").await;
24//! let name: Option<String> = context.get("name").await;
25//! let active: Option<bool> = context.get("active").await;
26//!
27//! // Synchronous access (useful in edge conditions)
28//! let name_sync: Option<String> = context.get_sync("name");
29//! # }
30//! ```
31//!
32//! ## Chat History Management
33//!
34//! ```rust
35//! use graph_flow::Context;
36//!
37//! # #[tokio::main]
38//! # async fn main() {
39//! let context = Context::new();
40//!
41//! // Add messages to chat history
42//! context.add_user_message("Hello, assistant!".to_string()).await;
43//! context.add_assistant_message("Hello! How can I help you?".to_string()).await;
44//! context.add_system_message("User session started".to_string()).await;
45//!
46//! // Get chat history
47//! let history = context.get_chat_history().await;
48//! let all_messages = context.get_all_messages().await;
49//! let last_5 = context.get_last_messages(5).await;
50//!
51//! // Check history status
52//! let count = context.chat_history_len().await;
53//! let is_empty = context.is_chat_history_empty().await;
54//! # }
55//! ```
56//!
57//! ## Context with Message Limits
58//!
59//! ```rust
60//! use graph_flow::Context;
61//!
62//! # #[tokio::main]
63//! # async fn main() {
64//! // Create context with maximum 100 messages
65//! let context = Context::with_max_chat_messages(100);
66//!
67//! // Messages will be automatically pruned when limit is exceeded
68//! for i in 0..150 {
69//!     context.add_user_message(format!("Message {}", i)).await;
70//! }
71//!
72//! // Only the last 100 messages are kept
73//! assert_eq!(context.chat_history_len().await, 100);
74//! # }
75//! ```
76//!
77//! ## LLM Integration (with `rig` feature)
78//!
79//! ```rust
80//! # #[cfg(feature = "rig")]
81//! # {
82//! use graph_flow::Context;
83//!
84//! # #[tokio::main]
85//! # async fn main() {
86//! let context = Context::new();
87//!
88//! context.add_user_message("What is the capital of France?".to_string()).await;
89//! context.add_assistant_message("The capital of France is Paris.".to_string()).await;
90//!
91//! // Get messages in rig format for LLM calls
92//! let rig_messages = context.get_rig_messages().await;
93//! let recent_messages = context.get_last_rig_messages(10).await;
94//!
95//! // Use with rig's completion API
96//! // let response = agent.completion(&rig_messages).await?;
97//! # }
98//! # }
99//! ```
100
101use chrono::{DateTime, Utc};
102use dashmap::DashMap;
103use serde::{Deserialize, Serialize};
104use serde_json::Value;
105use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
106
107use crate::error::GraphError;
108
109#[cfg(feature = "rig")]
110use rig::completion::Message;
111
112/// Represents the role of a message in a conversation.
113///
114/// Used in chat history to distinguish between different types of messages.
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
116pub enum MessageRole {
117    /// Message from a user/human
118    User,
119    /// Message from an assistant/AI
120    Assistant,
121    /// System message (instructions, status updates, etc.)
122    System,
123}
124
125/// A serializable message that can be converted to/from rig::completion::Message.
126///
127/// This struct provides a unified message format that can be stored, serialized,
128/// and optionally converted to other formats like rig's Message type.
129///
130/// # Examples
131///
132/// ```rust
133/// use graph_flow::{SerializableMessage, MessageRole};
134///
135/// // Create different types of messages
136/// let user_msg = SerializableMessage::user("Hello!".to_string());
137/// let assistant_msg = SerializableMessage::assistant("Hi there!".to_string());
138/// let system_msg = SerializableMessage::system("Session started".to_string());
139///
140/// // Access message properties
141/// assert_eq!(user_msg.role, MessageRole::User);
142/// assert_eq!(user_msg.content, "Hello!");
143/// ```
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct SerializableMessage {
146    /// The role of the message sender
147    pub role: MessageRole,
148    /// The content of the message
149    pub content: String,
150    /// When the message was created
151    pub timestamp: DateTime<Utc>,
152}
153
154impl SerializableMessage {
155    /// Create a new message with the specified role and content.
156    ///
157    /// The timestamp is automatically set to the current UTC time.
158    pub fn new(role: MessageRole, content: String) -> Self {
159        Self {
160            role,
161            content,
162            timestamp: Utc::now(),
163        }
164    }
165
166    /// Create a new user message.
167    ///
168    /// # Examples
169    ///
170    /// ```rust
171    /// use graph_flow::SerializableMessage;
172    ///
173    /// let msg = SerializableMessage::user("Hello, world!".to_string());
174    /// ```
175    pub fn user(content: String) -> Self {
176        Self::new(MessageRole::User, content)
177    }
178
179    /// Create a new assistant message.
180    ///
181    /// # Examples
182    ///
183    /// ```rust
184    /// use graph_flow::SerializableMessage;
185    ///
186    /// let msg = SerializableMessage::assistant("Hello! How can I help?".to_string());
187    /// ```
188    pub fn assistant(content: String) -> Self {
189        Self::new(MessageRole::Assistant, content)
190    }
191
192    /// Create a new system message.
193    ///
194    /// # Examples
195    ///
196    /// ```rust
197    /// use graph_flow::SerializableMessage;
198    ///
199    /// let msg = SerializableMessage::system("User logged in".to_string());
200    /// ```
201    pub fn system(content: String) -> Self {
202        Self::new(MessageRole::System, content)
203    }
204}
205
206/// Container for managing chat history with serialization support.
207///
208/// Provides automatic message limit management and convenient methods
209/// for adding and retrieving messages.
210///
211/// # Examples
212///
213/// ```rust
214/// use graph_flow::ChatHistory;
215///
216/// let mut history = ChatHistory::new();
217/// history.add_user_message("Hello".to_string());
218/// history.add_assistant_message("Hi there!".to_string());
219///
220/// assert_eq!(history.len(), 2);
221/// assert!(!history.is_empty());
222/// ```
223#[derive(Debug, Clone, Serialize, Deserialize, Default)]
224pub struct ChatHistory {
225    messages: Vec<SerializableMessage>,
226    max_messages: Option<usize>,
227}
228
229impl ChatHistory {
230    /// Create a new empty chat history with a default limit of 1000 messages.
231    pub fn new() -> Self {
232        Self {
233            messages: Vec::new(),
234            max_messages: Some(1000), // Default limit to prevent unbounded growth
235        }
236    }
237
238    /// Create a new chat history with a maximum message limit.
239    ///
240    /// When the limit is exceeded, older messages are automatically removed.
241    ///
242    /// # Examples
243    ///
244    /// ```rust
245    /// use graph_flow::ChatHistory;
246    ///
247    /// let mut history = ChatHistory::with_max_messages(10);
248    ///
249    /// // Add 15 messages
250    /// for i in 0..15 {
251    ///     history.add_user_message(format!("Message {}", i));
252    /// }
253    ///
254    /// // Only the last 10 are kept
255    /// assert_eq!(history.len(), 10);
256    /// ```
257    pub fn with_max_messages(max: usize) -> Self {
258        Self {
259            messages: Vec::new(),
260            max_messages: Some(max),
261        }
262    }
263
264    /// Add a user message to the chat history.
265    pub fn add_user_message(&mut self, content: String) {
266        self.add_message(SerializableMessage::user(content));
267    }
268
269    /// Add an assistant message to the chat history.
270    pub fn add_assistant_message(&mut self, content: String) {
271        self.add_message(SerializableMessage::assistant(content));
272    }
273
274    /// Add a system message to the chat history.
275    pub fn add_system_message(&mut self, content: String) {
276        self.add_message(SerializableMessage::system(content));
277    }
278
279    /// Add a message to the chat history, respecting max_messages limit.
280    fn add_message(&mut self, message: SerializableMessage) {
281        self.messages.push(message);
282
283        if let Some(max) = self.max_messages
284            && self.messages.len() > max
285        {
286            self.messages.drain(0..(self.messages.len() - max));
287        }
288    }
289
290    /// Clear all messages from the chat history.
291    pub fn clear(&mut self) {
292        self.messages.clear();
293    }
294
295    /// Get the number of messages in the chat history.
296    pub fn len(&self) -> usize {
297        self.messages.len()
298    }
299
300    /// Check if the chat history is empty.
301    pub fn is_empty(&self) -> bool {
302        self.messages.is_empty()
303    }
304
305    /// Get a reference to all messages.
306    pub fn messages(&self) -> &[SerializableMessage] {
307        &self.messages
308    }
309
310    /// Get the last N messages.
311    ///
312    /// If N is greater than the total number of messages, all messages are returned.
313    ///
314    /// # Examples
315    ///
316    /// ```rust
317    /// use graph_flow::ChatHistory;
318    ///
319    /// let mut history = ChatHistory::new();
320    /// history.add_user_message("Message 1".to_string());
321    /// history.add_user_message("Message 2".to_string());
322    /// history.add_user_message("Message 3".to_string());
323    ///
324    /// let last_two = history.last_messages(2);
325    /// assert_eq!(last_two.len(), 2);
326    /// assert_eq!(last_two[0].content, "Message 2");
327    /// assert_eq!(last_two[1].content, "Message 3");
328    /// ```
329    pub fn last_messages(&self, n: usize) -> &[SerializableMessage] {
330        let start = if self.messages.len() > n {
331            self.messages.len() - n
332        } else {
333            0
334        };
335        &self.messages[start..]
336    }
337}
338
339/// Helper struct for serializing/deserializing Context
340#[derive(Serialize, Deserialize)]
341struct ContextData {
342    data: std::collections::HashMap<String, Value>,
343    chat_history: ChatHistory,
344}
345
346/// Context for sharing data between tasks in a graph execution.
347///
348/// Provides thread-safe storage for workflow state and dedicated chat history
349/// management. The context is shared across all tasks in a workflow execution.
350///
351/// # Examples
352///
353/// ## Basic Usage
354///
355/// ```rust
356/// use graph_flow::Context;
357///
358/// # #[tokio::main]
359/// # async fn main() {
360/// let context = Context::new();
361///
362/// // Store different types of data
363/// context.set("user_id", 12345).await;
364/// context.set("name", "Alice".to_string()).await;
365/// context.set("settings", vec!["opt1", "opt2"]).await;
366///
367/// // Retrieve data
368/// let user_id: Option<i32> = context.get("user_id").await;
369/// let name: Option<String> = context.get("name").await;
370/// let settings: Option<Vec<String>> = context.get("settings").await;
371/// # }
372/// ```
373///
374/// ## Chat History
375///
376/// ```rust
377/// use graph_flow::Context;
378///
379/// # #[tokio::main]
380/// # async fn main() {
381/// let context = Context::new();
382///
383/// // Add messages
384/// context.add_user_message("Hello".to_string()).await;
385/// context.add_assistant_message("Hi there!".to_string()).await;
386///
387/// // Get message history
388/// let history = context.get_chat_history().await;
389/// let last_5 = context.get_last_messages(5).await;
390/// # }
391/// ```
392#[derive(Clone, Debug)]
393pub struct Context {
394    data: Arc<DashMap<String, Value>>,
395    chat_history: Arc<RwLock<ChatHistory>>,
396}
397
398impl Context {
399    /// Create a new empty context.
400    pub fn new() -> Self {
401        Self {
402            data: Arc::new(DashMap::new()),
403            chat_history: Arc::new(RwLock::new(ChatHistory::new())),
404        }
405    }
406
407    /// Create a new context with a maximum chat history size.
408    ///
409    /// When the chat history exceeds this size, older messages are automatically removed.
410    ///
411    /// # Examples
412    ///
413    /// ```rust
414    /// use graph_flow::Context;
415    ///
416    /// # #[tokio::main]
417    /// # async fn main() {
418    /// let context = Context::with_max_chat_messages(50);
419    ///
420    /// // Chat history will be limited to 50 messages
421    /// for i in 0..100 {
422    ///     context.add_user_message(format!("Message {}", i)).await;
423    /// }
424    ///
425    /// assert_eq!(context.chat_history_len().await, 50);
426    /// # }
427    /// ```
428    pub fn with_max_chat_messages(max: usize) -> Self {
429        Self {
430            data: Arc::new(DashMap::new()),
431            chat_history: Arc::new(RwLock::new(ChatHistory::with_max_messages(max))),
432        }
433    }
434
435    /// Acquire the chat history read lock, recovering from poisoning.
436    ///
437    /// A poisoned lock means a task panicked while holding it; the history
438    /// itself is still valid (messages are appended atomically), so we recover
439    /// the guard rather than silently returning empty data.
440    fn history_read(&self) -> RwLockReadGuard<'_, ChatHistory> {
441        self.chat_history
442            .read()
443            .unwrap_or_else(|poisoned| poisoned.into_inner())
444    }
445
446    /// Acquire the chat history write lock, recovering from poisoning (see
447    /// [`Context::history_read`]).
448    fn history_write(&self) -> RwLockWriteGuard<'_, ChatHistory> {
449        self.chat_history
450            .write()
451            .unwrap_or_else(|poisoned| poisoned.into_inner())
452    }
453
454    // Regular context methods (unchanged API)
455
456    /// Set a value in the context.
457    ///
458    /// The value must be serializable. Most common Rust types are supported.
459    ///
460    /// # Examples
461    ///
462    /// ```rust
463    /// use graph_flow::Context;
464    /// use serde::{Serialize, Deserialize};
465    ///
466    /// #[derive(Serialize, Deserialize)]
467    /// struct UserData {
468    ///     id: u32,
469    ///     name: String,
470    /// }
471    ///
472    /// # #[tokio::main]
473    /// # async fn main() {
474    /// let context = Context::new();
475    ///
476    /// // Store primitive types
477    /// context.set("count", 42).await;
478    /// context.set("name", "Alice".to_string()).await;
479    /// context.set("active", true).await;
480    ///
481    /// // Store complex types
482    /// let user = UserData { id: 1, name: "Bob".to_string() };
483    /// context.set("user", user).await;
484    /// # }
485    /// ```
486    ///
487    /// # Panics
488    ///
489    /// Panics if the value cannot be serialized to JSON (e.g. a map with
490    /// non-string keys). Use [`Context::try_set`] to handle this as an error.
491    pub async fn set(&self, key: impl Into<String>, value: impl serde::Serialize) {
492        self.set_sync(key, value);
493    }
494
495    /// Fallible version of [`Context::set`].
496    ///
497    /// Returns `Err(GraphError::ContextError)` instead of panicking when the
498    /// value cannot be serialized to JSON.
499    pub async fn try_set(
500        &self,
501        key: impl Into<String>,
502        value: impl serde::Serialize,
503    ) -> crate::error::Result<()> {
504        self.try_set_sync(key, value)
505    }
506
507    /// Synchronous, fallible version of [`Context::set`] (see [`Context::try_set`]).
508    pub fn try_set_sync(
509        &self,
510        key: impl Into<String>,
511        value: impl serde::Serialize,
512    ) -> crate::error::Result<()> {
513        let key = key.into();
514        let value = serde_json::to_value(value).map_err(|e| {
515            GraphError::ContextError(format!("Failed to serialize value for key '{key}': {e}"))
516        })?;
517        self.data.insert(key, value);
518        Ok(())
519    }
520
521    /// Get a value from the context.
522    ///
523    /// Returns `None` if the key doesn't exist or if deserialization fails.
524    ///
525    /// # Examples
526    ///
527    /// ```rust
528    /// use graph_flow::Context;
529    ///
530    /// # #[tokio::main]
531    /// # async fn main() {
532    /// let context = Context::new();
533    /// context.set("count", 42).await;
534    ///
535    /// let count: Option<i32> = context.get("count").await;
536    /// assert_eq!(count, Some(42));
537    ///
538    /// let missing: Option<String> = context.get("missing").await;
539    /// assert_eq!(missing, None);
540    /// # }
541    /// ```
542    pub async fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
543        self.get_sync(key)
544    }
545
546    /// Remove a value from the context.
547    ///
548    /// Returns the removed value if it existed.
549    ///
550    /// # Examples
551    ///
552    /// ```rust
553    /// use graph_flow::Context;
554    ///
555    /// # #[tokio::main]
556    /// # async fn main() {
557    /// let context = Context::new();
558    /// context.set("temp", "value".to_string()).await;
559    ///
560    /// let removed = context.remove("temp").await;
561    /// assert!(removed.is_some());
562    ///
563    /// let value: Option<String> = context.get("temp").await;
564    /// assert_eq!(value, None);
565    /// # }
566    /// ```
567    pub async fn remove(&self, key: &str) -> Option<Value> {
568        self.data.remove(key).map(|(_, v)| v)
569    }
570
571    /// Clear all regular context data (does not affect chat history).
572    ///
573    /// # Examples
574    ///
575    /// ```rust
576    /// use graph_flow::Context;
577    ///
578    /// # #[tokio::main]
579    /// # async fn main() {
580    /// let context = Context::new();
581    /// context.set("key1", "value1".to_string()).await;
582    /// context.set("key2", "value2".to_string()).await;
583    /// context.add_user_message("Hello".to_string()).await;
584    ///
585    /// context.clear().await;
586    ///
587    /// // Regular data is cleared
588    /// let value: Option<String> = context.get("key1").await;
589    /// assert_eq!(value, None);
590    ///
591    /// // Chat history is preserved
592    /// assert_eq!(context.chat_history_len().await, 1);
593    /// # }
594    /// ```
595    pub async fn clear(&self) {
596        self.data.clear();
597    }
598
599    /// Synchronous version of get for use in edge conditions.
600    ///
601    /// This method should only be used when you're certain the data exists
602    /// and when async is not available (e.g., in edge condition closures).
603    ///
604    /// # Examples
605    ///
606    /// ```rust
607    /// use graph_flow::{Context, GraphBuilder};
608    ///
609    /// # #[tokio::main]
610    /// # async fn main() {
611    /// let context = Context::new();
612    /// context.set("condition", true).await;
613    ///
614    /// // Used in edge conditions
615    /// let graph = GraphBuilder::new("test")
616    ///     .add_conditional_edge(
617    ///         "task1",
618    ///         |ctx| ctx.get_sync::<bool>("condition").unwrap_or(false),
619    ///         "task2",
620    ///         "task3"
621    ///     );
622    /// # }
623    /// ```
624    pub fn get_sync<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
625        let entry = self.data.get(key)?;
626        // Deserialize straight from a reference to avoid cloning the stored
627        // Value (which can be large, e.g. documents or chat transcripts).
628        match T::deserialize(entry.value()) {
629            Ok(value) => Some(value),
630            Err(e) => {
631                tracing::warn!(
632                    key = %key,
633                    error = %e,
634                    "Context value exists but failed to deserialize to the requested type"
635                );
636                None
637            }
638        }
639    }
640
641    /// Synchronous version of set for use when async is not available.
642    ///
643    /// # Examples
644    ///
645    /// ```rust
646    /// use graph_flow::Context;
647    ///
648    /// let context = Context::new();
649    /// context.set_sync("key", "value".to_string());
650    ///
651    /// let value: Option<String> = context.get_sync("key");
652    /// assert_eq!(value, Some("value".to_string()));
653    /// ```
654    ///
655    /// # Panics
656    ///
657    /// Panics if the value cannot be serialized to JSON. Use
658    /// [`Context::try_set_sync`] to handle this as an error.
659    pub fn set_sync(&self, key: impl Into<String>, value: impl serde::Serialize) {
660        let value = serde_json::to_value(value).expect("Failed to serialize value");
661        self.data.insert(key.into(), value);
662    }
663
664    // Chat history methods
665
666    /// Add a user message to the chat history.
667    ///
668    /// # Examples
669    ///
670    /// ```rust
671    /// use graph_flow::Context;
672    ///
673    /// # #[tokio::main]
674    /// # async fn main() {
675    /// let context = Context::new();
676    /// context.add_user_message("Hello, assistant!".to_string()).await;
677    /// # }
678    /// ```
679    pub async fn add_user_message(&self, content: String) {
680        self.history_write().add_user_message(content);
681    }
682
683    /// Add an assistant message to the chat history.
684    ///
685    /// # Examples
686    ///
687    /// ```rust
688    /// use graph_flow::Context;
689    ///
690    /// # #[tokio::main]
691    /// # async fn main() {
692    /// let context = Context::new();
693    /// context.add_assistant_message("Hello! How can I help you?".to_string()).await;
694    /// # }
695    /// ```
696    pub async fn add_assistant_message(&self, content: String) {
697        self.history_write().add_assistant_message(content);
698    }
699
700    /// Add a system message to the chat history.
701    ///
702    /// # Examples
703    ///
704    /// ```rust
705    /// use graph_flow::Context;
706    ///
707    /// # #[tokio::main]
708    /// # async fn main() {
709    /// let context = Context::new();
710    /// context.add_system_message("Session started".to_string()).await;
711    /// # }
712    /// ```
713    pub async fn add_system_message(&self, content: String) {
714        self.history_write().add_system_message(content);
715    }
716
717    /// Get a clone of the current chat history.
718    ///
719    /// # Examples
720    ///
721    /// ```rust
722    /// use graph_flow::Context;
723    ///
724    /// # #[tokio::main]
725    /// # async fn main() {
726    /// let context = Context::new();
727    /// context.add_user_message("Hello".to_string()).await;
728    ///
729    /// let history = context.get_chat_history().await;
730    /// assert_eq!(history.len(), 1);
731    /// # }
732    /// ```
733    pub async fn get_chat_history(&self) -> ChatHistory {
734        self.history_read().clone()
735    }
736
737    /// Clear the chat history.
738    ///
739    /// # Examples
740    ///
741    /// ```rust
742    /// use graph_flow::Context;
743    ///
744    /// # #[tokio::main]
745    /// # async fn main() {
746    /// let context = Context::new();
747    /// context.add_user_message("Hello".to_string()).await;
748    /// assert_eq!(context.chat_history_len().await, 1);
749    ///
750    /// context.clear_chat_history().await;
751    /// assert_eq!(context.chat_history_len().await, 0);
752    /// # }
753    /// ```
754    pub async fn clear_chat_history(&self) {
755        self.history_write().clear();
756    }
757
758    /// Get the number of messages in the chat history.
759    pub async fn chat_history_len(&self) -> usize {
760        self.history_read().len()
761    }
762
763    /// Check if the chat history is empty.
764    pub async fn is_chat_history_empty(&self) -> bool {
765        self.history_read().is_empty()
766    }
767
768    /// Get the last N messages from chat history.
769    ///
770    /// # Examples
771    ///
772    /// ```rust
773    /// use graph_flow::Context;
774    ///
775    /// # #[tokio::main]
776    /// # async fn main() {
777    /// let context = Context::new();
778    /// context.add_user_message("Message 1".to_string()).await;
779    /// context.add_user_message("Message 2".to_string()).await;
780    /// context.add_user_message("Message 3".to_string()).await;
781    ///
782    /// let last_two = context.get_last_messages(2).await;
783    /// assert_eq!(last_two.len(), 2);
784    /// assert_eq!(last_two[0].content, "Message 2");
785    /// assert_eq!(last_two[1].content, "Message 3");
786    /// # }
787    /// ```
788    pub async fn get_last_messages(&self, n: usize) -> Vec<SerializableMessage> {
789        self.history_read().last_messages(n).to_vec()
790    }
791
792    /// Get all messages from chat history as SerializableMessage.
793    ///
794    /// # Examples
795    ///
796    /// ```rust
797    /// use graph_flow::Context;
798    ///
799    /// # #[tokio::main]
800    /// # async fn main() {
801    /// let context = Context::new();
802    /// context.add_user_message("Hello".to_string()).await;
803    /// context.add_assistant_message("Hi there!".to_string()).await;
804    ///
805    /// let all_messages = context.get_all_messages().await;
806    /// assert_eq!(all_messages.len(), 2);
807    /// # }
808    /// ```
809    pub async fn get_all_messages(&self) -> Vec<SerializableMessage> {
810        self.history_read().messages().to_vec()
811    }
812
813    // Rig integration methods (only available when rig feature is enabled)
814
815    #[cfg(feature = "rig")]
816    /// Get all chat history messages converted to rig::completion::Message format.
817    ///
818    /// This method is only available when the "rig" feature is enabled.
819    ///
820    /// # Examples
821    ///
822    /// ```rust
823    /// # #[cfg(feature = "rig")]
824    /// # {
825    /// use graph_flow::Context;
826    ///
827    /// # #[tokio::main]
828    /// # async fn main() {
829    /// let context = Context::new();
830    /// context.add_user_message("Hello".to_string()).await;
831    /// context.add_assistant_message("Hi there!".to_string()).await;
832    ///
833    /// let rig_messages = context.get_rig_messages().await;
834    /// assert_eq!(rig_messages.len(), 2);
835    /// # }
836    /// # }
837    /// ```
838    pub async fn get_rig_messages(&self) -> Vec<Message> {
839        self.get_all_messages()
840            .await
841            .into_iter()
842            .map(Message::from)
843            .collect()
844    }
845
846    #[cfg(feature = "rig")]
847    /// Get the last N messages converted to rig::completion::Message format.
848    ///
849    /// This method is only available when the "rig" feature is enabled.
850    ///
851    /// # Examples
852    ///
853    /// ```rust
854    /// # #[cfg(feature = "rig")]
855    /// # {
856    /// use graph_flow::Context;
857    ///
858    /// # #[tokio::main]
859    /// # async fn main() {
860    /// let context = Context::new();
861    /// for i in 0..10 {
862    ///     context.add_user_message(format!("Message {}", i)).await;
863    /// }
864    ///
865    /// let last_5 = context.get_last_rig_messages(5).await;
866    /// assert_eq!(last_5.len(), 5);
867    /// # }
868    /// # }
869    /// ```
870    pub async fn get_last_rig_messages(&self, n: usize) -> Vec<Message> {
871        self.get_last_messages(n)
872            .await
873            .into_iter()
874            .map(Message::from)
875            .collect()
876    }
877}
878
879#[cfg(feature = "rig")]
880impl From<SerializableMessage> for Message {
881    /// Convert a [`SerializableMessage`] into a `rig::completion::Message`,
882    /// moving the content instead of cloning it.
883    ///
884    /// Only available when the "rig" feature is enabled.
885    fn from(msg: SerializableMessage) -> Self {
886        match msg.role {
887            MessageRole::User => Message::user(msg.content),
888            MessageRole::Assistant => Message::assistant(msg.content),
889            MessageRole::System => Message::system(msg.content),
890        }
891    }
892}
893
894impl Default for Context {
895    fn default() -> Self {
896        Self::new()
897    }
898}
899
900// Serialization support for Context
901impl Serialize for Context {
902    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
903    where
904        S: serde::Serializer,
905    {
906        // Convert DashMap to HashMap for serialization
907        let data: std::collections::HashMap<String, Value> = self
908            .data
909            .iter()
910            .map(|entry| (entry.key().clone(), entry.value().clone()))
911            .collect();
912
913        let chat_history = self.history_read().clone();
914
915        let context_data = ContextData { data, chat_history };
916        context_data.serialize(serializer)
917    }
918}
919
920impl<'de> Deserialize<'de> for Context {
921    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
922    where
923        D: serde::Deserializer<'de>,
924    {
925        let context_data = ContextData::deserialize(deserializer)?;
926
927        let data = Arc::new(DashMap::new());
928        for (key, value) in context_data.data {
929            data.insert(key, value);
930        }
931
932        let chat_history = Arc::new(RwLock::new(context_data.chat_history));
933
934        Ok(Context { data, chat_history })
935    }
936}
937
938#[cfg(test)]
939mod tests {
940    use super::*;
941
942    #[tokio::test]
943    async fn test_basic_context_operations() {
944        let context = Context::new();
945
946        context.set("key", "value").await;
947        let value: Option<String> = context.get("key").await;
948        assert_eq!(value, Some("value".to_string()));
949    }
950
951    #[tokio::test]
952    async fn test_chat_history_operations() {
953        let context = Context::new();
954
955        assert!(context.is_chat_history_empty().await);
956        assert_eq!(context.chat_history_len().await, 0);
957
958        context.add_user_message("Hello".to_string()).await;
959        context.add_assistant_message("Hi there!".to_string()).await;
960
961        assert!(!context.is_chat_history_empty().await);
962        assert_eq!(context.chat_history_len().await, 2);
963
964        let history = context.get_chat_history().await;
965        assert_eq!(history.len(), 2);
966        assert_eq!(history.messages()[0].content, "Hello");
967        assert_eq!(history.messages()[0].role, MessageRole::User);
968        assert_eq!(history.messages()[1].content, "Hi there!");
969        assert_eq!(history.messages()[1].role, MessageRole::Assistant);
970    }
971
972    #[tokio::test]
973    async fn test_chat_history_max_messages() {
974        let context = Context::with_max_chat_messages(2);
975
976        context.add_user_message("Message 1".to_string()).await;
977        context
978            .add_assistant_message("Response 1".to_string())
979            .await;
980        context.add_user_message("Message 2".to_string()).await;
981
982        let history = context.get_chat_history().await;
983        assert_eq!(history.len(), 2);
984        assert_eq!(history.messages()[0].content, "Response 1");
985        assert_eq!(history.messages()[1].content, "Message 2");
986    }
987
988    #[tokio::test]
989    async fn test_last_messages() {
990        let context = Context::new();
991
992        context.add_user_message("Message 1".to_string()).await;
993        context
994            .add_assistant_message("Response 1".to_string())
995            .await;
996        context.add_user_message("Message 2".to_string()).await;
997        context
998            .add_assistant_message("Response 2".to_string())
999            .await;
1000
1001        let last_two = context.get_last_messages(2).await;
1002        assert_eq!(last_two.len(), 2);
1003        assert_eq!(last_two[0].content, "Message 2");
1004        assert_eq!(last_two[1].content, "Response 2");
1005    }
1006
1007    #[tokio::test]
1008    async fn test_context_serialization() {
1009        let context = Context::new();
1010        context.set("key", "value").await;
1011        context.add_user_message("test message".to_string()).await;
1012
1013        let serialized = serde_json::to_string(&context).unwrap();
1014        let deserialized: Context = serde_json::from_str(&serialized).unwrap();
1015
1016        let value: Option<String> = deserialized.get("key").await;
1017        assert_eq!(value, Some("value".to_string()));
1018
1019        assert_eq!(deserialized.chat_history_len().await, 1);
1020        let history = deserialized.get_chat_history().await;
1021        assert_eq!(history.messages()[0].content, "test message");
1022        assert_eq!(history.messages()[0].role, MessageRole::User);
1023    }
1024
1025    #[test]
1026    fn test_serializable_message() {
1027        let msg = SerializableMessage::user("test content".to_string());
1028        assert_eq!(msg.role, MessageRole::User);
1029        assert_eq!(msg.content, "test content");
1030
1031        let serialized = serde_json::to_string(&msg).unwrap();
1032        let deserialized: SerializableMessage = serde_json::from_str(&serialized).unwrap();
1033
1034        assert_eq!(msg.role, deserialized.role);
1035        assert_eq!(msg.content, deserialized.content);
1036    }
1037
1038    #[test]
1039    fn test_chat_history_serialization() {
1040        let mut history = ChatHistory::new();
1041        history.add_user_message("Hello".to_string());
1042        history.add_assistant_message("Hi!".to_string());
1043
1044        let serialized = serde_json::to_string(&history).unwrap();
1045        let deserialized: ChatHistory = serde_json::from_str(&serialized).unwrap();
1046
1047        assert_eq!(deserialized.len(), 2);
1048        assert_eq!(deserialized.messages()[0].content, "Hello");
1049        assert_eq!(deserialized.messages()[1].content, "Hi!");
1050    }
1051
1052    #[cfg(feature = "rig")]
1053    #[tokio::test]
1054    async fn test_rig_integration() {
1055        let context = Context::new();
1056
1057        context.add_user_message("Hello".to_string()).await;
1058        context.add_assistant_message("Hi there!".to_string()).await;
1059        context
1060            .add_system_message("System message".to_string())
1061            .await;
1062
1063        let rig_messages = context.get_rig_messages().await;
1064        assert_eq!(rig_messages.len(), 3);
1065
1066        // Verify each message maps to the correct rig variant.
1067        assert!(
1068            matches!(&rig_messages[0], Message::User { .. }),
1069            "Expected Message::User, got: {:?}",
1070            rig_messages[0]
1071        );
1072        assert!(
1073            matches!(&rig_messages[1], Message::Assistant { .. }),
1074            "Expected Message::Assistant, got: {:?}",
1075            rig_messages[1]
1076        );
1077        assert!(
1078            matches!(&rig_messages[2], Message::System { content } if content == "System message"),
1079            "Expected Message::System with correct content, got: {:?}",
1080            rig_messages[2]
1081        );
1082
1083        // Verify get_last_rig_messages tail ordering when the tail includes a system message.
1084        let last_two = context.get_last_rig_messages(2).await;
1085        assert_eq!(last_two.len(), 2);
1086        assert!(
1087            matches!(&last_two[0], Message::Assistant { .. }),
1088            "Expected Message::Assistant as second-to-last, got: {:?}",
1089            last_two[0]
1090        );
1091        assert!(
1092            matches!(&last_two[1], Message::System { .. }),
1093            "Expected Message::System as last, got: {:?}",
1094            last_two[1]
1095        );
1096    }
1097}