use std::sync::Arc;
use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use smooth_operator_core::{CheckpointStore, KnowledgeBase};
use crate::access_control::AccessContext;
use crate::domain::{Conversation, Message, Participant, Session, SessionStatus};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConversationUpdate {
pub name: Option<String>,
pub metadata_json: Option<serde_json::Value>,
pub analytics_json: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionUpdate {
pub status: Option<SessionStatus>,
pub token_count: Option<u64>,
pub message_count: Option<u64>,
pub last_activity_at: Option<chrono::DateTime<chrono::Utc>>,
pub ended_at: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessagePage {
pub messages: Vec<Message>,
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageQuery {
pub conversation_id: String,
pub limit: usize,
pub cursor: Option<String>,
pub descending: bool,
}
impl MessageQuery {
pub fn new(conversation_id: impl Into<String>, limit: usize) -> Self {
Self {
conversation_id: conversation_id.into(),
limit,
cursor: None,
descending: false,
}
}
}
#[async_trait]
pub trait StorageAdapter: Send + Sync {
async fn create_conversation(&self, conversation: Conversation) -> Result<Conversation>;
async fn get_conversation(&self, id: &str) -> Result<Option<Conversation>>;
async fn list_conversations_by_org(&self, organization_id: &str) -> Result<Vec<Conversation>>;
async fn update_conversation(
&self,
id: &str,
update: ConversationUpdate,
) -> Result<Conversation>;
async fn add_participant(&self, participant: Participant) -> Result<Participant>;
async fn get_participant(&self, id: &str) -> Result<Option<Participant>>;
async fn list_participants_by_conversation(
&self,
conversation_id: &str,
) -> Result<Vec<Participant>>;
async fn resolve_participant_by_external_id(
&self,
conversation_id: &str,
external_id: &str,
) -> Result<Option<Participant>>;
async fn append_message(&self, message: Message) -> Result<Message>;
async fn get_message(&self, id: &str) -> Result<Option<Message>>;
async fn list_messages_by_conversation(&self, query: MessageQuery) -> Result<MessagePage>;
async fn create_session(&self, session: Session) -> Result<Session>;
async fn get_session(&self, session_id: &str) -> Result<Option<Session>>;
async fn update_session(&self, session_id: &str, update: SessionUpdate) -> Result<Session>;
async fn list_sessions_by_conversation(&self, conversation_id: &str) -> Result<Vec<Session>>;
fn checkpoints(&self) -> Arc<dyn CheckpointStore>;
fn knowledge(&self) -> Arc<dyn KnowledgeBase>;
fn knowledge_for_access(&self, access: &AccessContext) -> Arc<dyn KnowledgeBase> {
crate::access_control::AclKnowledgeStore::new(self.knowledge()).reader(access.clone())
}
}