use std::{ fmt::{Display, Formatter}, sync::Arc, }; use async_trait::async_trait; use chrono::{DateTime, Utc}; use rand::RngCore; use serde::{Deserialize, Serialize}; use serde_json::{Map as JsonMap, Value}; #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)] pub struct ConversationId(pub String); impl ConversationId { pub fn new() -> Self { let mut rng = rand::rng(); let mut bytes = [0u8; 24]; rng.fill_bytes(&mut bytes); let hex_string: String = bytes.iter().map(|b| format!("{:02x}", b)).collect(); Self(format!("conv_{}", hex_string)) } } impl Default for ConversationId { fn default() -> Self { Self::new() } } impl From for ConversationId { fn from(value: String) -> Self { Self(value) } } impl From<&str> for ConversationId { fn from(value: &str) -> Self { Self(value.to_string()) } } impl Display for ConversationId { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&self.0) } } /// Metadata payload persisted with a conversation pub type ConversationMetadata = JsonMap; /// Input payload for creating a conversation #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct NewConversation { #[serde(default, skip_serializing_if = "Option::is_none")] pub metadata: Option, } /// Stored conversation data structure #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Conversation { pub id: ConversationId, pub created_at: DateTime, #[serde(default, skip_serializing_if = "Option::is_none")] pub metadata: Option, } impl Conversation { pub fn new(new_conversation: NewConversation) -> Self { Self { id: ConversationId::new(), created_at: Utc::now(), metadata: new_conversation.metadata, } } pub fn with_parts( id: ConversationId, created_at: DateTime, metadata: Option, ) -> Self { Self { id, created_at, metadata, } } } /// Result alias for conversation storage operations pub type Result = std::result::Result; /// Error type for conversation storage operations #[derive(Debug, thiserror::Error)] pub enum ConversationStorageError { #[error("Conversation not found: {0}")] ConversationNotFound(String), #[error("Storage error: {0}")] StorageError(String), #[error("Serialization error: {0}")] SerializationError(#[from] serde_json::Error), } /// Trait describing the CRUD interface for conversation storage backends #[async_trait] pub trait ConversationStorage: Send + Sync + 'static { async fn create_conversation(&self, input: NewConversation) -> Result; async fn get_conversation(&self, id: &ConversationId) -> Result>; async fn update_conversation( &self, id: &ConversationId, metadata: Option, ) -> Result>; async fn delete_conversation(&self, id: &ConversationId) -> Result; } /// Shared pointer alias for conversation storage pub type SharedConversationStorage = Arc;