agentic_core/storage/
conversation.rs1use std::convert::TryFrom;
4use std::sync::Arc;
5
6use super::models::{conversation, item, response};
7use super::pool::DbPool;
8use super::types::{ConversationData, InOutItem, ResponseMetadata, StorageError, StoreResult};
9use crate::utils::common::{serialize_to_string, uuid7_str};
10
11#[derive(Clone, Debug)]
13pub struct ConversationStore {
14 pool: Option<Arc<DbPool>>,
15}
16
17impl ConversationStore {
18 #[must_use]
20 pub fn disabled() -> Self {
21 Self { pool: None }
22 }
23
24 #[must_use]
26 pub fn new(pool: Arc<DbPool>) -> Self {
27 Self { pool: Some(pool) }
28 }
29
30 fn pool(&self) -> StoreResult<&DbPool> {
36 self.pool.as_deref().ok_or(StorageError::NotConfigured)
37 }
38
39 pub async fn create(&self) -> StoreResult<ConversationData> {
45 let pool = self.pool()?;
46 let row = conversation::create(pool, &uuid7_str("conv_")).await?;
47 Ok(row.into())
48 }
49
50 pub async fn get_or_create(&self, conversation_id: &str) -> StoreResult<ConversationData> {
56 let pool = self.pool()?;
57 let row = conversation::get_or_create(pool, conversation_id).await?;
58 Ok(row.into())
59 }
60
61 pub async fn get(&self, conversation_id: &str) -> StoreResult<ConversationData> {
67 let pool = self.pool()?;
68 let row = conversation::get(pool, conversation_id)
69 .await?
70 .ok_or_else(|| StorageError::not_found("Conversation", conversation_id))?;
71 Ok(row.into())
72 }
73
74 pub async fn rehydrate(&self, conversation_id: &str) -> StoreResult<Vec<InOutItem>> {
80 let pool = self.pool()?;
81 let rows = item::get_items_by_conversation(pool, conversation_id).await?;
82
83 Ok(rows.into_iter().filter_map(|row| row.as_inout()).collect())
84 }
85
86 pub async fn persist(
94 &self,
95 conversation_id: &str,
96 response_id: &str,
97 previous_response_id: Option<&str>,
98 new_items: Vec<InOutItem>,
99 metadata: &ResponseMetadata,
100 ) -> StoreResult<()> {
101 let pool = self.pool()?;
102
103 let mut item_ids: Vec<String> = Vec::new();
104 let mut items_: Vec<(String, String)> = Vec::new();
105 for any_item in new_items {
106 let item_id = uuid7_str("item_");
107 item_ids.push(item_id.clone());
108 let data_str = String::try_from(&any_item)?;
109 items_.push((item_id, data_str));
110 }
111
112 let mut tx = pool.begin().await?;
113
114 let seq_start = item::conversation_item_count(&mut tx, conversation_id)
115 .await?
116 .ok_or_else(|| StorageError::not_found("Conversation", conversation_id))?;
117
118 item::create_in_tx(&mut tx, items_, Some(conversation_id), Some(seq_start)).await?;
119
120 let history_item_ids_json = serialize_to_string(&item_ids)?;
121 let metadata_json = String::try_from(metadata)?;
122
123 response::create_in_tx(
124 &mut tx,
125 response_id,
126 Some(conversation_id),
127 previous_response_id,
128 Some(&history_item_ids_json),
129 Some(&metadata_json),
130 )
131 .await?;
132 tx.commit().await?;
133
134 Ok(())
135 }
136}