agentic_core/storage/models/
conversation.rs1use super::super::pool::{DbPool, DbResult, DbTransaction};
4use crate::storage::backend::DatabaseBackend;
5use crate::utils::common::utcnow_str;
6
7#[derive(Debug, Clone, sqlx::FromRow)]
12pub struct Conversation {
13 pub id: String,
15
16 pub metadata: Option<String>,
18
19 pub created_at: i64,
21}
22
23pub async fn create(pool: &DbPool, id: &str) -> DbResult<Conversation> {
28 let now = utcnow_str();
29 sqlx::query_as::<_, Conversation>(
30 "INSERT INTO conversations (id, created_at) \
31 VALUES ($1, $2) RETURNING *",
32 )
33 .bind(id)
34 .bind(now)
35 .fetch_one(pool)
36 .await
37}
38
39pub async fn get_or_create(pool: &DbPool, id: &str) -> DbResult<Conversation> {
44 let now = utcnow_str();
45 sqlx::query_as::<_, Conversation>(
46 "INSERT INTO conversations (id, created_at) \
47 VALUES ($1, $2) \
48 ON CONFLICT (id) DO UPDATE SET created_at = created_at \
49 RETURNING *",
50 )
51 .bind(id)
52 .bind(now)
53 .fetch_one(pool)
54 .await
55}
56
57pub async fn get(pool: &DbPool, id: &str) -> DbResult<Option<Conversation>> {
62 sqlx::query_as::<_, Conversation>("SELECT * FROM conversations WHERE id = $1")
63 .bind(id)
64 .fetch_optional(pool)
65 .await
66}
67
68pub async fn lock_in_tx(tx: &mut DbTransaction<'_>, id: &str) -> DbResult<()> {
78 if DatabaseBackend::from_connection(tx.as_mut()) == DatabaseBackend::Postgres {
79 let locked_id = sqlx::query_scalar::<_, String>("SELECT id FROM conversations WHERE id = $1 FOR UPDATE")
80 .bind(id)
81 .fetch_optional(&mut **tx)
82 .await?;
83 return locked_id.map(|_| ()).ok_or(sqlx::Error::RowNotFound);
84 }
85
86 let result = sqlx::query("UPDATE conversations SET created_at = created_at WHERE id = $1")
87 .bind(id)
88 .execute(&mut **tx)
89 .await?;
90 if result.rows_affected() == 0 {
91 return Err(sqlx::Error::RowNotFound);
92 }
93 Ok(())
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_conversation_basic() {
102 let conversation = Conversation {
103 id: "conv_1".to_string(),
104 metadata: None,
105 created_at: 1_704_067_200,
106 };
107
108 assert_eq!(conversation.id, "conv_1");
109 assert!(conversation.metadata.is_none());
110 assert_eq!(conversation.created_at, 1_704_067_200);
111 }
112}