replace python django backend with nodejs backend
This commit is contained in:
5
backend_old/README.md
Normal file
5
backend_old/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Run dev env: `./dev.sh`
|
||||
|
||||
Migrate `python manage.py migrate`
|
||||
|
||||
Make migration `python manage.py makemigrations --name <name> ai`
|
||||
0
backend_old/__init__.py
Normal file
0
backend_old/__init__.py
Normal file
0
backend_old/ai/__init__.py
Normal file
0
backend_old/ai/__init__.py
Normal file
3
backend_old/ai/admin.py
Normal file
3
backend_old/ai/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
51
backend_old/ai/ais/AiBase.py
Normal file
51
backend_old/ai/ais/AiBase.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
|
||||
class AiBase:
|
||||
model_id_or_path: str = None
|
||||
model = None
|
||||
tokenizer = None
|
||||
|
||||
def __init__(self, model_id_or_path: str = None):
|
||||
self.model_id_or_path = model_id_or_path
|
||||
|
||||
print('Loading ' + model_id_or_path)
|
||||
self.model_id_or_path = model_id_or_path
|
||||
self.model = AutoModelForCausalLM.from_pretrained(model_id_or_path, torch_dtype='auto', device_map='auto')
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(model_id_or_path)
|
||||
# print(self.tokenizer.default_chat_template)
|
||||
# print(type(self.model))
|
||||
# print(type(self.tokenizer))
|
||||
print('Loaded')
|
||||
|
||||
def generate(self, messages):
|
||||
return []
|
||||
|
||||
def record_conversation(self, messages, response):
|
||||
messages = messages + [response]
|
||||
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
conversations_folder = this_dir + '/../../../conversations'
|
||||
folder = conversations_folder + '/' + self.model_id_or_path.replace('/', '_')
|
||||
self._mkdir(conversations_folder)
|
||||
self._mkdir(folder)
|
||||
timestamp = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')
|
||||
pickle_filename = folder + '/' + timestamp + '.json'
|
||||
with open(pickle_filename, 'w') as file:
|
||||
json.dump(messages, file)
|
||||
|
||||
def _mkdir(self, path):
|
||||
if not os.path.isdir(path):
|
||||
os.mkdir(path)
|
||||
|
||||
def __del__(self):
|
||||
del self.model
|
||||
del self.tokenizer
|
||||
|
||||
import gc
|
||||
gc.collect()
|
||||
torch.cuda.empty_cache()
|
||||
38
backend_old/ai/ais/QwenAi.py
Normal file
38
backend_old/ai/ais/QwenAi.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import torch
|
||||
from .AiBase import AiBase
|
||||
|
||||
|
||||
class QwenAi(AiBase):
|
||||
default_device = 'cuda' # the device to load the model onto
|
||||
default_model_id = 'Qwen/Qwen1.5-1.8B-Chat'
|
||||
|
||||
default_instruction = {'role': 'system',
|
||||
'name': 'system',
|
||||
'content': 'Your name is "Laura". You are an AI created by Alice.'}
|
||||
|
||||
def __init__(self, model_id_or_path=default_model_id):
|
||||
super().__init__(model_id_or_path)
|
||||
|
||||
def generate(self, messages):
|
||||
try:
|
||||
# prepare
|
||||
messages = [m for m in messages if m['role'] != 'system']
|
||||
input_messages = [self.default_instruction] + messages
|
||||
|
||||
# generate
|
||||
text = self.tokenizer.apply_chat_template(input_messages, tokenize=False, add_generation_prompt=True)
|
||||
model_inputs = self.tokenizer([text], return_tensors='pt').to(self.default_device)
|
||||
generated_ids = self.model.generate(model_inputs.input_ids, max_new_tokens=300)
|
||||
generated_ids = [
|
||||
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
|
||||
]
|
||||
response = self.tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
|
||||
|
||||
# add response and save conversation
|
||||
response_entry = {'role': 'assistant', 'name': 'assistant', 'content': response}
|
||||
messages.append(response_entry)
|
||||
self.record_conversation(input_messages, response_entry)
|
||||
|
||||
return messages
|
||||
finally:
|
||||
torch.cuda.empty_cache() # clear cache or the gpu mem will be used a lot
|
||||
0
backend_old/ai/ais/__init__.py
Normal file
0
backend_old/ai/ais/__init__.py
Normal file
11
backend_old/ai/apps.py
Normal file
11
backend_old/ai/apps.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AisConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'ai'
|
||||
|
||||
def ready(self):
|
||||
# noinspection PyUnresolvedReferences
|
||||
from . import events # register events
|
||||
pass
|
||||
46
backend_old/ai/event_listeners.py
Normal file
46
backend_old/ai/event_listeners.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from injector import Injector
|
||||
|
||||
from .eventsold import ChatTextEvent
|
||||
from .websocket import DashboardConnectionManager
|
||||
|
||||
|
||||
class EventListener:
|
||||
def handle(self, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ChatTextListener(EventListener):
|
||||
def handle(self, event):
|
||||
print('ChatTextListener')
|
||||
get_manager().broadcast({'type': 'chat_text', 'message': event.message, 'ai_instance': event.ai_name})
|
||||
|
||||
|
||||
def get_manager():
|
||||
from .injector import InjectorModule
|
||||
inj = Injector(InjectorModule)
|
||||
return inj.get(DashboardConnectionManager)
|
||||
|
||||
|
||||
ChatTextEvent.register(ChatTextListener())
|
||||
|
||||
# @injector.inject
|
||||
# def on_chat_text(manager: DashboardConnectionManager, ai_instance: AiInstance, message):
|
||||
# manager.broadcast({'type': 'chat_text', 'message': message, 'ai_instance': ai_instance.configuration.name})
|
||||
#
|
||||
#
|
||||
# @injector.inject
|
||||
# def on_discord_online(manager: DashboardConnectionManager, ai_instance: AiInstance, status: bool):
|
||||
# manager.broadcast({'type': 'discord_online', 'status': status, 'ai_instance': ai_instance.configuration.name})
|
||||
#
|
||||
#
|
||||
# @injector.inject
|
||||
# def on_discord_react_to_chat(manager: DashboardConnectionManager, ai_instance: AiInstance, status: bool):
|
||||
# manager.broadcast({'type': 'discord_react_to_chat',
|
||||
# 'status': status,
|
||||
# 'ai_instance': ai_instance.configuration.name})
|
||||
#
|
||||
|
||||
# def register_events():
|
||||
# Dispatcher.RegisterHandler('event::chat_text', on_chat_text)
|
||||
# Dispatcher.RegisterHandler('event::discord::online', on_discord_online)
|
||||
# Dispatcher.RegisterHandler('event::discord::react_to_chat', on_discord_react_to_chat)
|
||||
30
backend_old/ai/events.py
Normal file
30
backend_old/ai/events.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from django.dispatch import receiver, Signal
|
||||
from injector import Injector
|
||||
|
||||
from .websocket import DashboardConnectionManager
|
||||
|
||||
chat_text_signal = Signal()
|
||||
discord_online_signal = Signal()
|
||||
discord_react_to_chat_signal = Signal()
|
||||
|
||||
|
||||
@receiver(chat_text_signal)
|
||||
def handle_chat_text(sender, **kwargs):
|
||||
get_manager().broadcast({'type': 'chat_text', 'message': kwargs['message'], 'ai_instance': kwargs['ai_name']})
|
||||
|
||||
|
||||
@receiver(discord_online_signal)
|
||||
def handle_discord_online(sender, **kwargs):
|
||||
get_manager().broadcast({'type': 'discord_online', 'status': kwargs['status'], 'ai_instance': kwargs['ai_name']})
|
||||
|
||||
|
||||
@receiver(discord_react_to_chat_signal)
|
||||
def handle_discord_react_to_chat(sender, **kwargs):
|
||||
get_manager().broadcast(
|
||||
{'type': 'discord_react_to_chat', 'status': kwargs['status'], 'ai_instance': kwargs['ai_name']})
|
||||
|
||||
|
||||
def get_manager():
|
||||
from .injector import InjectorModule
|
||||
inj = Injector(InjectorModule)
|
||||
return inj.get(DashboardConnectionManager)
|
||||
11
backend_old/ai/injector.py
Normal file
11
backend_old/ai/injector.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from injector import Binder, Module, singleton
|
||||
|
||||
from .services.AiService import AiService
|
||||
from .websocket import DashboardConnectionManager
|
||||
|
||||
|
||||
class InjectorModule(Module):
|
||||
def configure(self, binder: Binder) -> None:
|
||||
self.__class__.instance = self
|
||||
binder.bind(AiService, scope=singleton)
|
||||
binder.bind(DashboardConnectionManager, scope=singleton)
|
||||
22
backend_old/ai/migrations/0001_initial.py
Normal file
22
backend_old/ai/migrations/0001_initial.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.0.4 on 2024-04-22 15:06
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AiConfig',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('model_id_or_path', models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
]
|
||||
19
backend_old/ai/migrations/0002_AddDicordToken.py
Normal file
19
backend_old/ai/migrations/0002_AddDicordToken.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.0.4 on 2024-04-23 17:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ai', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='aiconfig',
|
||||
name='discord_token',
|
||||
field=models.CharField(default='', max_length=255),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
0
backend_old/ai/migrations/__init__.py
Normal file
0
backend_old/ai/migrations/__init__.py
Normal file
11
backend_old/ai/models.py
Normal file
11
backend_old/ai/models.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class AiConfig(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
model_id_or_path = models.CharField(max_length=255)
|
||||
discord_token = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
19
backend_old/ai/serializers.py
Normal file
19
backend_old/ai/serializers.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from rest_framework import serializers
|
||||
from .models import AiConfig
|
||||
|
||||
|
||||
class AiConfigSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = AiConfig
|
||||
fields = ['name', 'model_id_or_path']
|
||||
|
||||
|
||||
class DiscordStatusSerializer(serializers.Serializer):
|
||||
online = serializers.BooleanField()
|
||||
react_to_chat = serializers.BooleanField()
|
||||
|
||||
|
||||
class AiInstanceSerializer(serializers.Serializer):
|
||||
configuration = AiConfigSerializer()
|
||||
messages = serializers.ListField()
|
||||
discord = DiscordStatusSerializer()
|
||||
83
backend_old/ai/services/AiInstance.py
Normal file
83
backend_old/ai/services/AiInstance.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import threading
|
||||
from typing import AnyStr
|
||||
|
||||
from .DcClient import DcClient
|
||||
from ..ais.AiBase import AiBase
|
||||
from ..ais.QwenAi import QwenAi
|
||||
from ..events import chat_text_signal, discord_online_signal, discord_react_to_chat_signal
|
||||
from ..models import AiConfig
|
||||
|
||||
|
||||
class DiscordStatus:
|
||||
def __init__(self, instance):
|
||||
self._instance = instance
|
||||
self._client = DcClient(instance)
|
||||
self.online = False
|
||||
self.react_to_chat = False
|
||||
|
||||
def set_online(self, online: bool):
|
||||
if online and not self.online:
|
||||
self._client.connect()
|
||||
elif not online and self.online:
|
||||
self._client.disconnect()
|
||||
else:
|
||||
return
|
||||
self.online = online
|
||||
discord_online_signal.send(sender=self.__class__, ai_name=self._instance.configuration.name, status=self.online)
|
||||
|
||||
def set_react_to_chat(self, react_to_chat: bool):
|
||||
self.react_to_chat = react_to_chat
|
||||
discord_react_to_chat_signal.send(sender=self.__class__, ai_name=self._instance.configuration.name,
|
||||
status=self.react_to_chat)
|
||||
|
||||
def __del__(self):
|
||||
self.set_online(False)
|
||||
|
||||
|
||||
class AiInstance:
|
||||
def __init__(self, configuration: AiConfig):
|
||||
print('Initializing AiInstance ' + configuration.name)
|
||||
self._bot: AiBase | None = None
|
||||
self._botLock = threading.Lock()
|
||||
self.configuration = configuration
|
||||
self.discord = DiscordStatus(self)
|
||||
self.messages = []
|
||||
self._messagesLock = threading.Lock()
|
||||
|
||||
def endure_loaded(self):
|
||||
self._botLock.acquire()
|
||||
try:
|
||||
if self._bot is None:
|
||||
self._bot = QwenAi(self.configuration.model_id_or_path)
|
||||
finally:
|
||||
self._botLock.release()
|
||||
|
||||
def endure_unloaded(self):
|
||||
self._botLock.acquire()
|
||||
try:
|
||||
if self._bot is not None:
|
||||
del self._bot
|
||||
self._bot = None
|
||||
finally:
|
||||
self._botLock.release()
|
||||
|
||||
def chat_text(self, user: AnyStr, text: AnyStr):
|
||||
self.endure_loaded()
|
||||
self._messagesLock.acquire()
|
||||
try:
|
||||
self.messages.append({'role': 'user', 'name': user, 'content': text})
|
||||
chat_text_signal.send(sender=self.__class__, ai_name=self.configuration.name, message=self.messages[-1])
|
||||
self.messages = self._bot.generate(self.messages)
|
||||
chat_text_signal.send(sender=self.__class__, ai_name=self.configuration.name, message=self.messages[-1])
|
||||
return self.messages[-1]
|
||||
finally:
|
||||
self._messagesLock.release()
|
||||
|
||||
def chat_conversation(self, conversation):
|
||||
self.endure_loaded()
|
||||
return self._bot.generate(conversation)
|
||||
|
||||
def __del__(self):
|
||||
print('Destructing AiInstance ' + self.configuration.name)
|
||||
del self._bot
|
||||
del self.discord
|
||||
34
backend_old/ai/services/AiService.py
Normal file
34
backend_old/ai/services/AiService.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from .AiInstance import AiInstance
|
||||
from ..models import AiConfig
|
||||
|
||||
|
||||
class AiService:
|
||||
# Singleton
|
||||
# def __new__(cls):
|
||||
# if not hasattr(cls, 'instance'):
|
||||
# cls.instance = super(AiService, cls).__new__(cls)
|
||||
# return cls.instance
|
||||
|
||||
def __init__(self):
|
||||
self.instances: [AiInstance] = []
|
||||
print('Initializing AiService')
|
||||
|
||||
def get_configurations(self) -> [AiConfig]:
|
||||
return AiConfig.objects.filter()
|
||||
|
||||
def get_instances(self) -> [AiInstance]:
|
||||
self.__ensure_instances_inited__()
|
||||
return self.instances
|
||||
|
||||
def get_instance(self, name: str) -> AiInstance:
|
||||
return [i for i in self.get_instances() if i.configuration.name == name][0]
|
||||
|
||||
def __ensure_instances_inited__(self):
|
||||
for config in self.get_configurations():
|
||||
if len([i for i in self.instances if i.configuration.name == config.name]) == 0:
|
||||
self.instances.append(AiInstance(config))
|
||||
|
||||
def __del__(self):
|
||||
print('Destructing AiService')
|
||||
for instance in self.instances:
|
||||
del instance
|
||||
44
backend_old/ai/services/DcClient.py
Normal file
44
backend_old/ai/services/DcClient.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from threading import Thread
|
||||
|
||||
from discord import Intents, Client
|
||||
|
||||
from . import AiInstance
|
||||
|
||||
|
||||
class DcClient:
|
||||
def __init__(self, _ai_instance: AiInstance):
|
||||
self._ai_instance = _ai_instance
|
||||
|
||||
self._intents = Intents.default()
|
||||
self._intents.message_content = True
|
||||
self._discord_client = Client(intents=self._intents)
|
||||
|
||||
@self._discord_client.event
|
||||
async def on_ready():
|
||||
print(f'We have logged in as {self._discord_client.user}')
|
||||
|
||||
@self._discord_client.event
|
||||
async def on_message(message):
|
||||
if not self._ai_instance.discord.react_to_chat:
|
||||
return None
|
||||
|
||||
if message.author == self._discord_client.user:
|
||||
return
|
||||
|
||||
if message.content.isspace():
|
||||
await message.channel.send('### Empty message')
|
||||
return
|
||||
|
||||
response = self._ai_instance.chat_text(message.author.name, message.content)
|
||||
if response is not None:
|
||||
await message.channel.send(response['content'])
|
||||
|
||||
def connect(self):
|
||||
def start():
|
||||
self._discord_client.run(self._ai_instance.configuration.discord_token)
|
||||
|
||||
thread = Thread(target=start)
|
||||
thread.start()
|
||||
|
||||
def disconnect(self):
|
||||
self._discord_client.close()
|
||||
3
backend_old/ai/tests.py
Normal file
3
backend_old/ai/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
18
backend_old/ai/urls.py
Normal file
18
backend_old/ai/urls.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from django.urls import path
|
||||
|
||||
from .views import AiConfigApiView, AiApiView, AiDiscordApiView, AiChatTextApiView
|
||||
from .websocket import DashboardConsumer
|
||||
|
||||
urlpatterns = [
|
||||
path('api/v1/ai/<name>/chat/text', AiChatTextApiView.as_view()),
|
||||
path('api/v1/ai/<name>/discord', AiDiscordApiView.as_view()),
|
||||
path('api/v1/ai/config', AiConfigApiView.as_view()),
|
||||
path('api/v1/ai', AiApiView.as_view()),
|
||||
]
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
'websocket': URLRouter([
|
||||
path('ws/dashboard/', DashboardConsumer.as_asgi()),
|
||||
])
|
||||
})
|
||||
87
backend_old/ai/views.py
Normal file
87
backend_old/ai/views.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from injector import inject
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from .serializers import AiConfigSerializer, AiInstanceSerializer
|
||||
from .services.AiService import AiService
|
||||
|
||||
|
||||
class AiApiView(APIView):
|
||||
# add permission to check if user is authenticated
|
||||
# permission_classes = [permissions.IsAuthenticated]
|
||||
ai_service: AiService = None
|
||||
|
||||
@inject
|
||||
def setup(self, request, ai_service: AiService, **kwargs):
|
||||
super().setup(request)
|
||||
self.ai_service = ai_service
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
instances = self.ai_service.get_instances()
|
||||
serializer = AiInstanceSerializer(instances, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class AiConfigApiView(APIView):
|
||||
# add permission to check if user is authenticated
|
||||
# permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
ai_service: AiService = None
|
||||
|
||||
@inject
|
||||
def setup(self, request, ai_service: AiService, **kwargs):
|
||||
super().setup(request)
|
||||
self.ai_service = ai_service
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
configurations = self.ai_service.get_configurations()
|
||||
serializer = AiConfigSerializer(configurations, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class AiChatTextApiView(APIView):
|
||||
# add permission to check if user is authenticated
|
||||
# permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
ai_service: AiService = None
|
||||
|
||||
@inject
|
||||
def setup(self, request, ai_service: AiService, **kwargs):
|
||||
super().setup(request)
|
||||
self.ai_service = ai_service
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
name = self.kwargs.get('name')
|
||||
print("name", name)
|
||||
ai_instance = self.ai_service.get_instance(name)
|
||||
if ai_instance is None:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
print(request.data)
|
||||
response = ai_instance.chat_text(request.data['name'], request.data['content'])
|
||||
return Response(response, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class AiDiscordApiView(APIView):
|
||||
# add permission to check if user is authenticated
|
||||
# permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
ai_service: AiService = None
|
||||
|
||||
@inject
|
||||
def setup(self, request, ai_service: AiService, **kwargs):
|
||||
super().setup(request)
|
||||
self.ai_service = ai_service
|
||||
|
||||
def patch(self, request, *args, **kwargs):
|
||||
name = self.kwargs.get('name')
|
||||
ai_instance = self.ai_service.get_instance(name)
|
||||
if ai_instance is None:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if 'online' in request.data:
|
||||
ai_instance.discord.set_online(request.data['online'])
|
||||
if 'react_to_chat' in request.data:
|
||||
ai_instance.discord.set_react_to_chat(request.data['react_to_chat'])
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
44
backend_old/ai/websocket.py
Normal file
44
backend_old/ai/websocket.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import injector
|
||||
import json
|
||||
from channels.generic.websocket import WebsocketConsumer
|
||||
|
||||
|
||||
class DashboardConnectionManager:
|
||||
def __init__(self):
|
||||
self._clients = []
|
||||
|
||||
def register(self, client: WebsocketConsumer):
|
||||
self._clients.append(client)
|
||||
pass
|
||||
|
||||
def unregister(self, client: WebsocketConsumer):
|
||||
self._clients.remove(client)
|
||||
|
||||
def broadcast(self, message: any):
|
||||
for client in self._clients:
|
||||
client.push(message)
|
||||
|
||||
|
||||
class DashboardConsumer(WebsocketConsumer):
|
||||
@injector.inject
|
||||
def __init__(self, manager: DashboardConnectionManager, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._manager = manager
|
||||
|
||||
def connect(self):
|
||||
self.accept()
|
||||
self._manager.register(self)
|
||||
|
||||
def disconnect(self, close_code):
|
||||
self._manager.unregister(self)
|
||||
|
||||
def receive(self, text_data=None, bytes_data=None):
|
||||
# text_data_json = json.loads(text_data)
|
||||
# message = text_data_json['message']
|
||||
# self.send(text_data=json.dumps({
|
||||
# 'message': message
|
||||
# }))
|
||||
pass # do nothing currently
|
||||
|
||||
def push(self, message):
|
||||
self.send(text_data=json.dumps(message))
|
||||
0
backend_old/backend/__init__.py
Normal file
0
backend_old/backend/__init__.py
Normal file
16
backend_old/backend/asgi.py
Normal file
16
backend_old/backend/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for backend project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
132
backend_old/backend/settings.py
Normal file
132
backend_old/backend/settings.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""
|
||||
Django settings for backend project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.0.4.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-a*pam^5+0)v!(#rz^s4cmi13iv==4_!8(v#xk%qnqkv+7z=rai'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_injector',
|
||||
'rest_framework',
|
||||
'ai',
|
||||
'channels',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
# 'django_injector.inject_request_middleware',
|
||||
]
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
'default': {
|
||||
'BACKEND': 'channels.layers.InMemoryChannelLayer',
|
||||
},
|
||||
}
|
||||
|
||||
ROOT_URLCONF = 'backend.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'backend.wsgi.application'
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
INJECTOR_MODULES = [
|
||||
'ai.injector.InjectorModule',
|
||||
]
|
||||
23
backend_old/backend/urls.py
Normal file
23
backend_old/backend/urls.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
URL configuration for backend project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/5.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('ai.urls')),
|
||||
]
|
||||
16
backend_old/backend/wsgi.py
Normal file
16
backend_old/backend/wsgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for backend project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
BIN
backend_old/db.sqlite3
Normal file
BIN
backend_old/db.sqlite3
Normal file
Binary file not shown.
1
backend_old/dev.sh
Executable file
1
backend_old/dev.sh
Executable file
@@ -0,0 +1 @@
|
||||
python manage.py runserver 0.0.0.0:8877
|
||||
22
backend_old/manage.py
Executable file
22
backend_old/manage.py
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user