From f2ff95562a22903b8b48e8c0ea1fea55a54d8529 Mon Sep 17 00:00:00 2001 From: Anulax Date: Mon, 19 May 2025 15:26:31 +0200 Subject: [PATCH] Replace View by adrf View --- .../commands/create_conversations.py | 15 +++++ .../0008_alter_conversation_user.py | 21 +++++++ api/models.py | 2 +- api/urls.py | 3 +- api/views.py | 56 ++++++++++--------- app/templates/index.html | 13 ++--- botzilla/settings.py | 1 + static/drf-yasg/style.css | 4 -- 8 files changed, 73 insertions(+), 42 deletions(-) create mode 100644 api/management/commands/create_conversations.py create mode 100644 api/migrations/0008_alter_conversation_user.py delete mode 100644 static/drf-yasg/style.css diff --git a/api/management/commands/create_conversations.py b/api/management/commands/create_conversations.py new file mode 100644 index 0000000..0829d2a --- /dev/null +++ b/api/management/commands/create_conversations.py @@ -0,0 +1,15 @@ +from django.core.management.base import BaseCommand +from api.models import Conversation + +class Command(BaseCommand): + help = 'Create N test conversations' + + def add_arguments(self, parser): + parser.add_argument('--number', type=int, help='Number of conversations to create') + + def handle(self, *args, **kwargs): + number = kwargs.get('number') or 1 + + for i in range(number): + conv = Conversation.objects.create(title="test-conversation") + self.stdout.write(self.style.SUCCESS(f'Successfully created test conversation (id : {conv.id})')) \ No newline at end of file diff --git a/api/migrations/0008_alter_conversation_user.py b/api/migrations/0008_alter_conversation_user.py new file mode 100644 index 0000000..f3455e9 --- /dev/null +++ b/api/migrations/0008_alter_conversation_user.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2.1 on 2025-05-19 09:49 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0007_conversation_user'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='conversation', + name='user', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='conversations', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/api/models.py b/api/models.py index 9e790cd..a7cb7cf 100644 --- a/api/models.py +++ b/api/models.py @@ -37,7 +37,7 @@ class AiModel(models.Model): class Conversation(models.Model): title = models.CharField(max_length=255) - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="conversations") + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="conversations", null=True) class Message(models.Model): content = models.TextField() diff --git a/api/urls.py b/api/urls.py index f29aa30..a3b18ee 100644 --- a/api/urls.py +++ b/api/urls.py @@ -22,7 +22,6 @@ from rest_framework_simplejwt.views import ( ) from .views import MasterKeyTokenObtainView from .views import ConversationView -from .views import ConversationActions from rest_framework.routers import DefaultRouter router = DefaultRouter() @@ -31,6 +30,6 @@ urlpatterns = [ path('token/', MasterKeyTokenObtainView.as_view(), name='token_obtain_pair'), path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('token/verify/', TokenVerifyView.as_view(), name='token_verify'), - path('conversations//prompt/', ConversationActions.as_view()), + #path('conversations//prompt/', conversation_prompt), path('', include(router.urls)), ] \ No newline at end of file diff --git a/api/views.py b/api/views.py index c4bcc06..d3daa60 100644 --- a/api/views.py +++ b/api/views.py @@ -1,31 +1,27 @@ from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi -import base64 -import json from rest_framework_simplejwt.views import TokenObtainPairView from rest_framework_simplejwt.tokens import RefreshToken -from django.views.decorators.http import require_http_methods from rest_framework.response import Response -from rest_framework import status from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework import status +from rest_framework.decorators import action, api_view +from adrf import viewsets +from django.views.decorators.http import require_POST from django.utils import timezone from django.contrib.auth import get_user_model -from .models import MasterKey -from .models import Conversation +from .models import MasterKey, Conversation from django.http import StreamingHttpResponse from .models import Message from .serializers import ConversationSerializer, MessageSerializer -from rest_framework import viewsets, status -from rest_framework.decorators import action -from rest_framework.response import Response + from .utils import add_swagger_summaries from django.views import View import ollama from asgiref.sync import sync_to_async from django.utils.decorators import method_decorator -import asyncio - +import asyncio, base64, json from django.views.decorators.csrf import csrf_exempt User = get_user_model() @@ -35,7 +31,7 @@ class ConversationView(viewsets.ModelViewSet): queryset = Conversation.objects.all() serializer_class = ConversationSerializer - permission_classes = [IsAuthenticated] # JWT token auth required + permission_classes = [AllowAny] # JWT token auth required def get_queryset(self): queryset = Conversation.objects.all() @@ -64,16 +60,14 @@ class ConversationView(viewsets.ModelViewSet): ] ) @action(detail=True, methods=['get']) - def contents(self, request, pk=None): + async def contents(self, request, pk=None): conversation = self.get_object() messages = conversation.messages.all() return Response(data=list(messages.values())) -@method_decorator(csrf_exempt, name='dispatch') -class ConversationActions(View): @swagger_auto_schema( operation_description="Discutes with the ai", - operation_summary="Make a new prompt", + operation_summary="Make a prompt", request_body=openapi.Schema( type=openapi.TYPE_OBJECT, properties={ @@ -82,14 +76,17 @@ class ConversationActions(View): required=['content'] ), responses={ - 200: openapi.Response('Message processed successfully', MessageSerializer), + 200: openapi.Response( + description="Text stream response", + schema=openapi.Schema(type=openapi.TYPE_STRING, format='binary') + ), 400: 'Bad Request', - } + }, + tags=['conversations'] ) - @sync_to_async - def post(self, request, *args, **kwargs): - conversation = Conversation.objects.get(pk=self.kwargs['id']) - data = json.loads(request.body) + @action(detail=True, methods=['post']) + def prompt(self, request, pk=None): + conversation = self.get_object() messages = []#{ # "role": "system", # "content": """ @@ -114,24 +111,24 @@ class ConversationActions(View): }) messages.append({ "role": "user", - "content": data['content'] or "" + "content": request.data.get('content', '') }) - Message(role="user", content=data['content'] or "", conversation=conversation).save() + Message(role="user", content=request.data.get('content', '') or "", conversation=conversation).save() ai_message = Message(role="assistant", content="", conversation=conversation) @sync_to_async def save_message(): ai_message.save() - + async def chat_event_stream(): message = "" try: - stream = ollama.chat(model="llama2-uncensored", messages=messages, stream=True) + stream = ollama.chat(model="gemma3:12b", messages=messages, stream=True) for chunk in stream: message = chunk['message']['content'] #print(message, base64.b64encode(message.encode("utf-8"))) ai_message.content += message - yield f"{base64.b64encode(message.encode("utf-8")).decode("utf-8")}" + yield f"{message}" finally: await save_message() response = StreamingHttpResponse(chat_event_stream(), content_type='text/event-stream') @@ -139,6 +136,11 @@ class ConversationActions(View): response['X-Accel-Buffering'] = 'no' return response + + + + + class MasterKeyTokenObtainView(TokenObtainPairView): diff --git a/app/templates/index.html b/app/templates/index.html index 7184d97..eee8d45 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -31,10 +31,6 @@ margin-top: 10px; border-radius: 5px; } - input { - padding: 8px; - width: 70%; - } button { padding: 8px 16px; background-color: #4CAF50; @@ -49,8 +45,8 @@ -

Simple Chat App

- +

Simple Chat App

+

Enter your message: