from django.contrib.auth.decorators import login_required from django.db.models import Count, Q from django.http import HttpRequest, HttpResponse, JsonResponse, StreamingHttpResponse from django.shortcuts import redirect, render from django.views.decorators.http import require_http_methods from .services import ( create_conversation, get_conversation_for_user, list_conversations, send_message, stream_message, ) from .models import Conversation, FileAttachment, FileSummaryBatch @login_required @require_http_methods(["GET", "POST"]) def workspace(request: HttpRequest) -> HttpResponse: """Renders the review-agent workspace and handles conversation actions.""" if request.method == "POST": action = request.POST.get("action") conversation = get_conversation_for_user(request.user, request.POST.get("conversation_id")) if action == "new_conversation": conversation = create_conversation(request.user) return redirect(f"/?conversation={conversation.pk}") if action == "send_message": content = (request.POST.get("prompt") or "").strip() if not conversation: conversation = create_conversation(request.user) if content: send_message(conversation, content) return redirect(f"/?conversation={conversation.pk}") search = (request.GET.get("q") or "").strip() conversations = list_conversations(request.user, search) current = get_conversation_for_user(request.user, request.GET.get("conversation")) if current is None and conversations.exists(): current = conversations.first() return render( request, "home.html", { "page_title": "审核智能体", "search_query": search, "conversations": conversations, "current_conversation": current, "messages": current.messages.all() if current else [], "attachments": FileAttachment.objects.filter(conversation=current).order_by("original_name", "-version_no") if current else [], "summary_batches": FileSummaryBatch.objects.filter(conversation=current).prefetch_related("node_runs").order_by("-created_at")[:5] if current else [], }, ) @login_required @require_http_methods(["GET"]) def attachment_manager(request: HttpRequest) -> HttpResponse: conversations = ( Conversation.objects.filter(user=request.user) .annotate( attachment_count=Count( "file_attachments", filter=~Q(file_attachments__upload_status=FileAttachment.UploadStatus.DELETED), ) ) .order_by("-updated_at", "-id") ) selected = get_conversation_for_user(request.user, request.GET.get("conversation")) attachments = ( FileAttachment.objects.filter(conversation=selected) .order_by("original_name", "-version_no") if selected else [] ) return render( request, "attachment_manager.html", { "page_title": "附件管理", "conversations": conversations, "selected_conversation": selected, "attachments": attachments, }, ) @login_required @require_http_methods(["POST"]) def stream_chat(request: HttpRequest) -> HttpResponse: """Streams one assistant reply so the UI can render incremental output.""" content = (request.POST.get("prompt") or "").strip() if not content: return JsonResponse({"error": "消息内容不能为空。"}, status=400) conversation = get_conversation_for_user(request.user, request.POST.get("conversation_id")) if not conversation: conversation = create_conversation(request.user) response = StreamingHttpResponse( streaming_content=stream_message(conversation, content), content_type="text/event-stream", ) response["Cache-Control"] = "no-cache" response["X-Accel-Buffering"] = "no" return response