159 lines
4.4 KiB
Python
159 lines
4.4 KiB
Python
"""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()
|