Skip to main content

engram/
message.rs

1//! `ChatMessage` type and `MessageStore` trait — chat message persistence.
2//!
3//! `MessageStore` stores raw conversation messages (role + content) grouped by
4//! `conversation_id`. This is separate from `FactStore`, which stores extracted
5//! facts derived from conversations. Agents use `MessageStore` to replay
6//! conversation history and provide context windows to LLMs.
7
8use async_trait::async_trait;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13use crate::scope::Scope;
14use crate::store::MemoryError;
15
16/// Unique identifier for a chat message.
17pub type MessageId = Uuid;
18
19/// A single chat message within a conversation.
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct ChatMessage {
22    pub id: MessageId,
23    pub conversation_id: String,
24    pub role: String,
25    pub content: String,
26    pub scope: Scope,
27    pub seq: i32,
28    pub created_at: DateTime<Utc>,
29    #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
30    pub metadata: serde_json::Map<String, serde_json::Value>,
31}
32
33impl ChatMessage {
34    pub fn new(
35        conversation_id: impl Into<String>,
36        role: impl Into<String>,
37        content: impl Into<String>,
38        scope: Scope,
39        seq: i32,
40    ) -> Self {
41        Self {
42            id: Uuid::new_v4(),
43            conversation_id: conversation_id.into(),
44            role: role.into(),
45            content: content.into(),
46            scope,
47            seq,
48            created_at: Utc::now(),
49            metadata: serde_json::Map::new(),
50        }
51    }
52}
53
54/// Persistence interface for chat messages.
55///
56/// Implementations MUST be `Send + Sync` so that `Arc<dyn MessageStore>` can be
57/// shared across async tasks.
58#[async_trait]
59pub trait MessageStore: Send + Sync {
60    /// Save one or more messages to a conversation. Returns the ids of the
61    /// saved messages.
62    async fn save_messages(
63        &self,
64        conversation_id: &str,
65        messages: &[ChatMessage],
66        scope: &Scope,
67    ) -> Result<Vec<MessageId>, MemoryError>;
68
69    /// Retrieve messages from a conversation, optionally limited to the last N
70    /// messages. Results are ordered by `seq` ascending.
71    async fn get_messages(
72        &self,
73        conversation_id: &str,
74        last_n: Option<usize>,
75        scope: &Scope,
76    ) -> Result<Vec<ChatMessage>, MemoryError>;
77
78    /// List all conversation ids visible to the given scope.
79    async fn list_conversations(&self, scope: &Scope) -> Result<Vec<String>, MemoryError>;
80
81    /// Delete all messages in a conversation. Returns the number of rows deleted.
82    async fn delete_messages(
83        &self,
84        conversation_id: &str,
85        scope: &Scope,
86    ) -> Result<u64, MemoryError>;
87}