gglib_core/services/
chat_history.rs

1//! Chat history service - thin orchestrator for chat operations.
2//!
3//! This service provides a clean interface for chat history operations,
4//! delegating all persistence to the `ChatHistoryRepository` port.
5
6use std::sync::Arc;
7
8use crate::domain::chat::{Conversation, ConversationUpdate, Message, NewConversation, NewMessage};
9use crate::ports::chat_history::{ChatHistoryError, ChatHistoryRepository};
10
11/// Service for managing chat history.
12///
13/// This is a thin orchestration layer over the `ChatHistoryRepository` port.
14/// It provides a clean API and handles any business logic that doesn't
15/// belong in the repository layer.
16pub struct ChatHistoryService {
17    repo: Arc<dyn ChatHistoryRepository>,
18}
19
20impl ChatHistoryService {
21    /// Create a new chat history service.
22    pub fn new(repo: Arc<dyn ChatHistoryRepository>) -> Self {
23        Self { repo }
24    }
25
26    /// Create a new conversation.
27    pub async fn create_conversation(
28        &self,
29        title: String,
30        model_id: Option<i64>,
31        system_prompt: Option<String>,
32    ) -> Result<i64, ChatHistoryError> {
33        self.repo
34            .create_conversation(NewConversation {
35                title,
36                model_id,
37                system_prompt,
38            })
39            .await
40    }
41
42    /// List all conversations, ordered by most recently updated.
43    pub async fn list_conversations(&self) -> Result<Vec<Conversation>, ChatHistoryError> {
44        self.repo.list_conversations().await
45    }
46
47    /// Get a specific conversation by ID.
48    pub async fn get_conversation(
49        &self,
50        id: i64,
51    ) -> Result<Option<Conversation>, ChatHistoryError> {
52        self.repo.get_conversation(id).await
53    }
54
55    /// Update conversation metadata.
56    pub async fn update_conversation(
57        &self,
58        id: i64,
59        new_title: Option<String>,
60        system_prompt: Option<Option<String>>,
61    ) -> Result<(), ChatHistoryError> {
62        self.repo
63            .update_conversation(
64                id,
65                ConversationUpdate {
66                    title: new_title,
67                    system_prompt,
68                },
69            )
70            .await
71    }
72
73    /// Delete a conversation and all its messages.
74    pub async fn delete_conversation(&self, id: i64) -> Result<(), ChatHistoryError> {
75        self.repo.delete_conversation(id).await
76    }
77
78    /// Get conversation count.
79    pub async fn get_conversation_count(&self) -> Result<i64, ChatHistoryError> {
80        self.repo.get_conversation_count().await
81    }
82
83    /// Get all messages for a conversation.
84    pub async fn get_messages(
85        &self,
86        conversation_id: i64,
87    ) -> Result<Vec<Message>, ChatHistoryError> {
88        self.repo.get_messages(conversation_id).await
89    }
90
91    /// Save a new message.
92    pub async fn save_message(&self, msg: NewMessage) -> Result<i64, ChatHistoryError> {
93        self.repo.save_message(msg).await
94    }
95
96    /// Update a message's content and optionally its metadata.
97    pub async fn update_message(
98        &self,
99        id: i64,
100        content: String,
101        metadata: Option<serde_json::Value>,
102    ) -> Result<(), ChatHistoryError> {
103        self.repo.update_message(id, content, metadata).await
104    }
105
106    /// Delete a message and all subsequent messages.
107    pub async fn delete_message_and_subsequent(&self, id: i64) -> Result<i64, ChatHistoryError> {
108        self.repo.delete_message_and_subsequent(id).await
109    }
110
111    /// Get message count for a conversation.
112    pub async fn get_message_count(&self, conversation_id: i64) -> Result<i64, ChatHistoryError> {
113        self.repo.get_message_count(conversation_id).await
114    }
115}