use serde::{Deserialize, Serialize}; /// The role of a participant in a chat conversation. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum ChatRole { /// Message sent by the human user User, /// Message generated by the AI assistant Assistant, /// System-level instruction (not displayed in UI) System, } /// Namespace for grouping chat sessions in the sidebar. /// /// Sessions are visually separated in the chat sidebar by namespace, /// with `News` sessions appearing under a dedicated "News Chats" header. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] pub enum ChatNamespace { /// General user-initiated chat conversations. #[default] General, /// Chats originating from news article follow-ups on the dashboard. News, } /// The type of file attached to a chat message. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum AttachmentKind { /// Image file (png, jpg, webp, etc.) Image, /// Document file (pdf, docx, txt, etc.) Document, /// Source code file Code, } /// A file attachment on a chat message. /// /// # Fields /// /// * `name` - Original filename /// * `kind` - Type of attachment for rendering /// * `size_bytes` - File size in bytes #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Attachment { pub name: String, pub kind: AttachmentKind, pub size_bytes: u64, } /// A persisted chat session stored in MongoDB. /// /// Messages are stored separately in the `chat_messages` collection /// and loaded on demand when the user opens a session. /// /// # Fields /// /// * `id` - MongoDB document ID (hex string) /// * `user_sub` - Keycloak subject ID (session owner) /// * `title` - Display title (auto-generated or user-renamed) /// * `namespace` - Grouping for sidebar sections /// * `provider` - LLM provider used (e.g. "ollama", "openai") /// * `model` - Model ID used (e.g. "llama3.1:8b") /// * `created_at` - ISO 8601 creation timestamp /// * `updated_at` - ISO 8601 last-activity timestamp /// * `article_url` - Source article URL (for News namespace sessions) #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ChatSession { #[serde(default, alias = "_id", skip_serializing_if = "String::is_empty")] pub id: String, pub user_sub: String, pub title: String, #[serde(default)] pub namespace: ChatNamespace, pub provider: String, pub model: String, pub created_at: String, pub updated_at: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub article_url: Option, } /// A single persisted message within a chat session. /// /// Stored in the `chat_messages` MongoDB collection, linked to a /// `ChatSession` via `session_id`. /// /// # Fields /// /// * `id` - MongoDB document ID (hex string) /// * `session_id` - Foreign key to `ChatSession.id` /// * `role` - Who sent this message /// * `content` - Message text content (may contain markdown) /// * `attachments` - File attachments (Phase 2, currently empty) /// * `timestamp` - ISO 8601 timestamp #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ChatMessage { #[serde(default, alias = "_id", skip_serializing_if = "String::is_empty")] pub id: String, pub session_id: String, pub role: ChatRole, pub content: String, #[serde(default)] pub attachments: Vec, pub timestamp: String, }