Skip to main content

agentic_core/storage/models/
conversation.rs

1//! Conversation context and history.
2
3use super::super::pool::{DbPool, DbResult};
4use crate::utils::common::utcnow_str;
5
6/// Conversation context and history.
7///
8/// Maps to the `conversations` table and represents a logical conversation
9/// containing multiple responses and items.
10#[derive(Debug, Clone, sqlx::FromRow)]
11pub struct Conversation {
12    /// Unique conversation identifier.
13    pub id: String,
14
15    /// Optional metadata as JSON string.
16    pub metadata: Option<String>,
17
18    /// Creation timestamp as Unix timestamp in seconds.
19    pub created_at: i64,
20}
21
22/// Create a new conversation.
23///
24/// # Errors
25/// Returns `DbResult::Err` if the database insertion fails.
26pub async fn create(pool: &DbPool, id: &str) -> DbResult<Conversation> {
27    let now = utcnow_str();
28    sqlx::query_as::<_, Conversation>(
29        "INSERT INTO conversations (id, created_at) \
30         VALUES (?, ?) RETURNING *",
31    )
32    .bind(id)
33    .bind(now)
34    .fetch_one(pool)
35    .await
36}
37
38/// Get or create a conversation.
39///
40/// # Errors
41/// Returns `DbResult::Err` if the database query fails.
42pub async fn get_or_create(pool: &DbPool, id: &str) -> DbResult<Conversation> {
43    let now = utcnow_str();
44    sqlx::query_as::<_, Conversation>(
45        "INSERT INTO conversations (id, created_at) \
46         VALUES (?, ?) \
47         ON CONFLICT (id) DO UPDATE SET created_at = created_at \
48         RETURNING *",
49    )
50    .bind(id)
51    .bind(now)
52    .fetch_one(pool)
53    .await
54}
55
56/// Get a conversation by ID.
57///
58/// # Errors
59/// Returns `DbResult::Err` if the database query fails.
60pub async fn get(pool: &DbPool, id: &str) -> DbResult<Option<Conversation>> {
61    sqlx::query_as::<_, Conversation>("SELECT * FROM conversations WHERE id = ?")
62        .bind(id)
63        .fetch_optional(pool)
64        .await
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_conversation_basic() {
73        let conversation = Conversation {
74            id: "conv_1".to_string(),
75            metadata: None,
76            created_at: 1_704_067_200,
77        };
78
79        assert_eq!(conversation.id, "conv_1");
80        assert!(conversation.metadata.is_none());
81        assert_eq!(conversation.created_at, 1_704_067_200);
82    }
83}