"""Message Service - handles message building and persistence""" import json import uuid import logging from typing import List, Dict, Any, Optional from luxx.core.database import SessionLocal from luxx.models import Conversation, Message logger = logging.getLogger(__name__) class MessageService: """Service for building and persisting messages""" def build_messages( self, conversation: Conversation, include_system: bool = True ) -> List[Dict[str, str]]: """ Build message list from conversation history. Args: conversation: Conversation object include_system: Whether to include system prompt Returns: List of message dicts with 'role' and 'content' keys """ messages = [] # Add system prompt if include_system and conversation.system_prompt: messages.append({ "role": "system", "content": conversation.system_prompt }) # Load messages from database db = SessionLocal() try: db_messages = db.query(Message).filter( Message.conversation_id == conversation.id ).order_by(Message.created_at).all() for msg in db_messages: content = self._parse_content(msg.content) messages.append({ "role": msg.role, "content": content }) finally: db.close() return messages def _parse_content(self, content: str) -> str: """Parse JSON content if possible, return plain content otherwise""" if not content: return "" try: content_obj = json.loads(content) if isinstance(content_obj, dict): return content_obj.get("text", content) return str(content_obj) except (json.JSONDecodeError, TypeError): return content def add_user_message( self, messages: List[Dict[str, str]], user_message: str, attachments: List[Dict] = None ) -> List[Dict[str, str]]: """ Add user message to the message list. Args: messages: Existing message list user_message: User's message text attachments: Optional list of attachments Returns: Updated message list """ content = { "text": user_message, "attachments": attachments or [] } messages.append({ "role": "user", "content": json.dumps(content, ensure_ascii=False) }) return messages def create_message_id(self) -> str: """Generate unique message ID""" return str(uuid.uuid4()) def save_assistant_message( self, conversation_id: int, msg_id: str, full_content: str, all_tool_calls: List[Dict], all_tool_results: List[Dict], all_steps: List[Dict], token_count: int = 0, usage: Optional[Dict] = None ) -> Optional[Message]: """ Save assistant message to database. Args: conversation_id: Conversation ID msg_id: Message UUID full_content: Full text content all_tool_calls: List of tool calls made all_tool_results: List of tool results all_steps: List of processing steps token_count: Token count usage: Token usage dict Returns: Created Message object or None on error """ content_json = { "text": full_content, "steps": all_steps } if all_tool_calls: content_json["tool_calls"] = all_tool_calls db = SessionLocal() try: msg = Message( id=msg_id, conversation_id=conversation_id, role="assistant", content=json.dumps(content_json, ensure_ascii=False), token_count=token_count, usage=json.dumps(usage) if usage else None ) db.add(msg) db.commit() return msg except Exception as e: db.rollback() logger.error(f"Failed to save message: {e}") return None finally: db.close() # Global service instance message_service = MessageService()