from __future__ import annotations from django.db.models import Q, QuerySet from django.utils import timezone from .llm import LLMConfigurationError, LLMRequestError, generate_reply from .models import Conversation, Message def list_conversations(user, search: str = "") -> QuerySet[Conversation]: """Returns a user's conversations, optionally filtered by title or content.""" conversations = Conversation.objects.filter(user=user) if not search: return conversations return conversations.filter( Q(title__icontains=search) | Q(messages__content__icontains=search) ).distinct() def get_conversation_for_user(user, conversation_id: int | None) -> Conversation | None: """Loads a conversation only when it belongs to the current user.""" if not conversation_id: return None return Conversation.objects.filter(user=user, pk=conversation_id).first() def create_conversation(user) -> Conversation: """Creates an empty conversation that can immediately accept messages.""" now = timezone.localtime() return Conversation.objects.create( user=user, title=f"新对话 {now.strftime('%m-%d %H:%M')}", ) def append_user_message(conversation: Conversation, content: str) -> Message: """Appends a user message and updates the conversation title from the first prompt.""" message = Message.objects.create( conversation=conversation, role=Message.Role.USER, content=content.strip(), ) if conversation.messages.filter(role=Message.Role.USER).count() == 1: conversation.title = build_conversation_title(content) conversation.save(update_fields=["title", "updated_at"]) return message def append_assistant_message(conversation: Conversation, content: str) -> Message: """Appends the deterministic assistant reply.""" return Message.objects.create( conversation=conversation, role=Message.Role.ASSISTANT, content=content, ) def send_message(conversation: Conversation, content: str) -> tuple[Message, Message]: """Stores one user message and one provider-backed assistant reply.""" user_message = append_user_message(conversation, content) try: reply_content = generate_reply(conversation, content) except (LLMConfigurationError, LLMRequestError) as exc: reply_content = f"模型调用失败:{exc}" assistant_message = append_assistant_message(conversation, reply_content) if conversation.title.startswith("新对话"): conversation.title = build_conversation_title(content) conversation.save(update_fields=["title", "updated_at"]) return user_message, assistant_message def build_conversation_title(content: str) -> str: """Creates a concise title from the first user message.""" normalized = " ".join(content.strip().split()) if not normalized: return "新对话" return normalized[:24]