//! Pure Conversation entity — in-memory message history plus system prompt //! and LLM generation parameters. //! //! # Architecture //! This is a pure data structure with **no I/O logic**. Load/save //! responsibilities live in [`ConversationRepository`](super::repository::ConversationRepository). #![allow( clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss, clippy::cast_possible_wrap )] use serde::{Deserialize, Serialize}; pub use zesdex_entities::seaorm::common::message::{ChatMessage, Role}; /// A single conversation's message history and generation settings. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Conversation { pub messages: Vec, pub system_prompt: String, pub session_id: String, pub model: String, pub max_tokens: Option, pub temperature: Option, } impl Conversation { /// Create an empty conversation with the given system prompt and /// session id, using default model / token / temperature settings. pub fn new(system_prompt: String, session_id: String) -> Self { Self { messages: Vec::new(), system_prompt, session_id, model: "anthropic/claude-opus-4-8".to_string(), max_tokens: None, temperature: None, } } /// Append a message to the conversation history. pub fn push(&mut self, msg: ChatMessage) { self.messages.push(msg); } /// Replace the system prompt and strip any prior `System`-role messages /// from history. pub fn rebuild_system(&mut self, new_prompt: String) { self.system_prompt = new_prompt; self.messages.retain(|m| !matches!(m.role, Role::System)); } /// Build the message list to send to the LLM API, with the system /// prompt prepended as the first message. pub fn to_api_messages(&self) -> Vec { let mut msgs = Vec::with_capacity(self.messages.len() + 1); msgs.push(ChatMessage::system(&self.system_prompt)); msgs.extend(self.messages.iter().cloned()); msgs } /// Number of messages in the conversation history (excluding the /// synthesized system message). pub fn len(&self) -> usize { self.messages.len() } /// Returns `true` if the conversation has no messages. pub fn is_empty(&self) -> bool { self.messages.is_empty() } } #[cfg(test)] mod tests { use super::*; #[test] fn chat_message_is_the_canonical_entities_type() { let canonical = zesdex_entities::seaorm::common::message::ChatMessage::user("hi"); let via_cms: ChatMessage = canonical; assert_eq!(via_cms.content.as_deref(), Some("hi")); } }