temp checkin
This commit is contained in:
41
README.md
41
README.md
@@ -1,3 +1,40 @@
|
||||
# chat_backend
|
||||
# Chat Bot Backend
|
||||
|
||||
Backend django project for the chat site
|
||||
## Setup
|
||||
|
||||
Clone the repo
|
||||
```console
|
||||
git clone http://10.0.0.160:3000/AI_ML_Operations_LLC/Chat_Bot_Backend.git
|
||||
```
|
||||
|
||||
Go into the repo
|
||||
```console
|
||||
cd Chat_Bot_Backend
|
||||
```
|
||||
|
||||
Create the virtual environment
|
||||
```console
|
||||
virtualenv --python=python3.9 venv
|
||||
```
|
||||
|
||||
Activate the Virtual Environment
|
||||
```console
|
||||
. venv/bin/activate
|
||||
```
|
||||
|
||||
Install the requirments
|
||||
```console
|
||||
python -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Pre-populate the database with test data
|
||||
|
||||
Run the dev server
|
||||
```console
|
||||
python manage.py
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Create inital database with temp data
|
||||
- [ ] Do a lot of stuff.....
|
||||
|
||||
8
llm_be/.idea/.gitignore
generated
vendored
Normal file
8
llm_be/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
6
llm_be/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
llm_be/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
6
llm_be/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
llm_be/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
27
llm_be/.idea/llm_be.iml
generated
Normal file
27
llm_be/.idea/llm_be.iml
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="llm_be/settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="migrations" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
</component>
|
||||
</module>
|
||||
7
llm_be/.idea/misc.xml
generated
Normal file
7
llm_be/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.10 (flex-backend-client)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (flex-backend-client)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
llm_be/.idea/modules.xml
generated
Normal file
8
llm_be/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/llm_be.iml" filepath="$PROJECT_DIR$/.idea/llm_be.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
llm_be/.idea/vcs.xml
generated
Normal file
6
llm_be/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
0
llm_be/chat_backend/__init__.py
Normal file
0
llm_be/chat_backend/__init__.py
Normal file
71
llm_be/chat_backend/admin.py
Normal file
71
llm_be/chat_backend/admin.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from django.contrib import admin
|
||||
from .models import CustomUser, Announcement, Company, LLMModels, Conversation, Prompt, Feedback, PromptMetric
|
||||
|
||||
# Register your models here.
|
||||
|
||||
|
||||
class AnnouncmentAdmin(admin.ModelAdmin):
|
||||
model = Announcement
|
||||
|
||||
|
||||
class CompanyAdmin(admin.ModelAdmin):
|
||||
model = Company
|
||||
|
||||
|
||||
class CustomUserAdmin(admin.ModelAdmin):
|
||||
model = CustomUser
|
||||
list_display = (
|
||||
"email",
|
||||
"username",
|
||||
"is_company_manager",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"is_active",
|
||||
"is_staff",
|
||||
"has_usable_password",
|
||||
"deleted",
|
||||
"has_signed_tos",
|
||||
"last_login",
|
||||
"slug",
|
||||
"get_set_password_url"
|
||||
)
|
||||
search_fields = ("fields", "username", "first_name", "last_name", "slug")
|
||||
|
||||
class FeedbackAdmin(admin.ModelAdmin):
|
||||
model = Feedback
|
||||
search_fields = ("status", "text", "get_user_email")
|
||||
list_display= (
|
||||
"status", "get_user_email", "title", "category"
|
||||
)
|
||||
|
||||
class LLMModelsAdmin(admin.ModelAdmin):
|
||||
model = LLMModels
|
||||
list_display = ("name", "port", "description")
|
||||
search_fields = ("name", "port", "description")
|
||||
|
||||
|
||||
class ConversationAdmin(admin.ModelAdmin):
|
||||
model = Conversation
|
||||
list_display = ("title", "get_user_email","deleted")
|
||||
search_fields = ("title",)
|
||||
|
||||
|
||||
class PromptAdmin(admin.ModelAdmin):
|
||||
model = Prompt
|
||||
list_display = ("message", "user_created", "get_conversation_title")
|
||||
search_fields = ("message",)
|
||||
|
||||
class PromptMetricAdmin(admin.ModelAdmin):
|
||||
model = PromptMetric
|
||||
list_display = ("event", "model_name", "prompt_length","reponse_length",'has_file','file_type', "get_duration")
|
||||
|
||||
|
||||
admin.site.register(Announcement, AnnouncmentAdmin)
|
||||
admin.site.register(Company, CompanyAdmin)
|
||||
admin.site.register(CustomUser, CustomUserAdmin)
|
||||
|
||||
admin.site.register(LLMModels, LLMModelsAdmin)
|
||||
admin.site.register(Conversation, ConversationAdmin)
|
||||
admin.site.register(Prompt, PromptAdmin)
|
||||
admin.site.register(PromptMetric, PromptMetricAdmin)
|
||||
admin.site.register(Feedback, FeedbackAdmin)
|
||||
6
llm_be/chat_backend/apps.py
Normal file
6
llm_be/chat_backend/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ChatBackendConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "chat_backend"
|
||||
30
llm_be/chat_backend/client.py
Normal file
30
llm_be/chat_backend/client.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
llama client - Abstract this in the future
|
||||
"""
|
||||
import ollama
|
||||
from typing import List, Dict
|
||||
|
||||
class LlamaClient(object):
|
||||
def __init__(self, model: str='llama3'):
|
||||
self.client = ollama.Client(host="http://127.0.0.1:11434")
|
||||
self.model = model
|
||||
|
||||
def check_if_model_exists(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def generate_conversation_title(self, message:str):
|
||||
response = self.generate_single_message("Summarise the phrase in one to for words\"%s\"" % message)
|
||||
|
||||
raw_response = response['response'].replace("\"","")
|
||||
return " ".join(raw_response.split()[:4])
|
||||
|
||||
def generate_single_message(self, message: str):
|
||||
return ollama.generate(model=self.model, prompt=message)
|
||||
|
||||
def get_chat_response(self, messages: List[str]):
|
||||
return self.client.chat(model = self.model, messages=messages, stream=False)
|
||||
|
||||
|
||||
def get_streamed_chat_response(self, messages: List[str]):
|
||||
return self.client.chat(model = self.model, messages=messages, stream=True)
|
||||
|
||||
158
llm_be/chat_backend/migrations/0001_initial.py
Normal file
158
llm_be/chat_backend/migrations/0001_initial.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# Generated by Django 3.2.18 on 2024-06-20 20:41
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Company",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.TextField(max_length=256)),
|
||||
("state", models.TextField(max_length=2)),
|
||||
("zipcode", models.TextField(max_length=5)),
|
||||
("address", models.TextField(max_length=256)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="CustomUser",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
||||
(
|
||||
"last_login",
|
||||
models.DateTimeField(
|
||||
blank=True, null=True, verbose_name="last login"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_superuser",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates that this user has all permissions without explicitly assigning them.",
|
||||
verbose_name="superuser status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"username",
|
||||
models.CharField(
|
||||
error_messages={
|
||||
"unique": "A user with that username already exists."
|
||||
},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.contrib.auth.validators.UnicodeUsernameValidator()
|
||||
],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="first name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"last_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="last name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
blank=True, max_length=254, verbose_name="email address"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates whether the user can log into this admin site.",
|
||||
verbose_name="staff status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_active",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
verbose_name="active",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="date joined"
|
||||
),
|
||||
),
|
||||
(
|
||||
"company",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="chat_backend.company",
|
||||
),
|
||||
),
|
||||
(
|
||||
"groups",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.Group",
|
||||
verbose_name="groups",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_permissions",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="Specific permissions for this user.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.Permission",
|
||||
verbose_name="user permissions",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "user",
|
||||
"verbose_name_plural": "users",
|
||||
"abstract": False,
|
||||
},
|
||||
managers=[
|
||||
("objects", django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
||||
43
llm_be/chat_backend/migrations/0002_accouncement.py
Normal file
43
llm_be/chat_backend/migrations/0002_accouncement.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 3.2.18 on 2024-06-21 19:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Accouncement",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"status",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("DEFAULT", "default"),
|
||||
("WARNING", "warning"),
|
||||
("INFO", "info"),
|
||||
("DANGER", "danger"),
|
||||
],
|
||||
default="DEFAULT",
|
||||
max_length=7,
|
||||
),
|
||||
),
|
||||
("message", models.TextField(max_length=256)),
|
||||
("start_date_time", models.DateTimeField(auto_now=True)),
|
||||
("end_date_time", models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 3.2.18 on 2024-06-21 19:30
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0002_accouncement"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="customuser",
|
||||
name="company",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="chat_backend.company",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 3.2.18 on 2024-06-21 19:32
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0003_alter_customuser_company"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name="Accouncement",
|
||||
new_name="Announcement",
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,162 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-02 15:15
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0004_rename_accouncement_announcement"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Conversation",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(default=django.utils.timezone.now)),
|
||||
(
|
||||
"last_modified",
|
||||
models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
(
|
||||
"title",
|
||||
models.CharField(
|
||||
default="",
|
||||
help_text="The title for the conversation",
|
||||
max_length=64,
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="LLMModels",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(default=django.utils.timezone.now)),
|
||||
(
|
||||
"last_modified",
|
||||
models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
("name", models.CharField(default="", max_length=254)),
|
||||
(
|
||||
"port",
|
||||
models.IntegerField(
|
||||
help_text="This specifies the port that the LLM runs on"
|
||||
),
|
||||
),
|
||||
(
|
||||
"description",
|
||||
models.CharField(
|
||||
default="",
|
||||
help_text="A description for the LLM. Limit is 512 characters",
|
||||
max_length=512,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="announcement",
|
||||
name="created",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="announcement",
|
||||
name="last_modified",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="company",
|
||||
name="created",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="company",
|
||||
name="last_modified",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Prompt",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(default=django.utils.timezone.now)),
|
||||
(
|
||||
"last_modified",
|
||||
models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
(
|
||||
"message",
|
||||
models.CharField(
|
||||
help_text="The text for a prompt", max_length=10240
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_created",
|
||||
models.BooleanField(
|
||||
help_text="True if was created by the user. False if it was generate by the LLM"
|
||||
),
|
||||
),
|
||||
(
|
||||
"conversation",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="chat_backend.conversation",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="company",
|
||||
name="available_llms",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
help_text="A list of LLMs that company can use",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="chat_backend.llmmodels",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-03 13:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0005_conversation_llmmodels_announcement_created_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="customuser",
|
||||
name="is_company_manager",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="Allows the edit/add/remove of users for a company",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-03 18:33
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0006_customuser_is_company_manager"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="prompt",
|
||||
name="conversation",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="chat_backend.conversation",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-03 18:46
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0007_alter_prompt_conversation"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="conversation",
|
||||
name="user",
|
||||
field=models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-03 18:48
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0008_alter_conversation_user"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="conversation",
|
||||
name="user",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-10 14:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0009_alter_conversation_user"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="conversation",
|
||||
name="deleted",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text="This is to hid accounts"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="customuser",
|
||||
name="deleted",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text="This is to hid accounts"
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2.13 on 2024-09-11 13:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0010_conversation_deleted_customuser_deleted"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="customuser",
|
||||
name="has_signed_tos",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text="If the user has signed the TOS"
|
||||
),
|
||||
),
|
||||
]
|
||||
18
llm_be/chat_backend/migrations/0012_customuser_slug.py
Normal file
18
llm_be/chat_backend/migrations/0012_customuser_slug.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.13 on 2024-09-11 18:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0011_customuser_has_signed_tos"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="customuser",
|
||||
name="slug",
|
||||
field=models.SlugField(default="luygzxpbkv"),
|
||||
),
|
||||
]
|
||||
19
llm_be/chat_backend/migrations/0013_alter_customuser_slug.py
Normal file
19
llm_be/chat_backend/migrations/0013_alter_customuser_slug.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.13 on 2024-09-11 18:28
|
||||
|
||||
import autoslug.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0012_customuser_slug"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="customuser",
|
||||
name="slug",
|
||||
field=autoslug.fields.AutoSlugField(editable=False, populate_from="email"),
|
||||
),
|
||||
]
|
||||
52
llm_be/chat_backend/migrations/0014_feedback.py
Normal file
52
llm_be/chat_backend/migrations/0014_feedback.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# Generated by Django 4.2.13 on 2024-09-14 18:29
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0013_alter_customuser_slug"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Feedback",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("text", models.TextField(max_length=512)),
|
||||
(
|
||||
"status",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("SUBMITTED", "Submitted"),
|
||||
("RESOLVED", "Resolved"),
|
||||
("DEFFERED", "Deffered"),
|
||||
("CLOSED", "Closed"),
|
||||
],
|
||||
default="SUBMITTED",
|
||||
max_length=24,
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 4.2.13 on 2024-09-15 01:39
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0014_feedback"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="feedback",
|
||||
name="category",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("NOT_DEFINED", "Not defined"),
|
||||
("BUG", "Bug"),
|
||||
("ENCHANCEMENT", "Enhancment"),
|
||||
],
|
||||
default="NOT_DEFINED",
|
||||
max_length=24,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="feedback",
|
||||
name="created",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="feedback",
|
||||
name="last_modified",
|
||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="feedback",
|
||||
name="title",
|
||||
field=models.TextField(default="", max_length=64),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,90 @@
|
||||
# Generated by Django 4.2.13 on 2024-12-30 15:55
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0015_feedback_category_feedback_created_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="PromptMetric",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(default=django.utils.timezone.now)),
|
||||
(
|
||||
"last_modified",
|
||||
models.DateTimeField(default=django.utils.timezone.now),
|
||||
),
|
||||
(
|
||||
"prompt_id",
|
||||
models.IntegerField(
|
||||
help_text="The id of the prompt this matches to"
|
||||
),
|
||||
),
|
||||
(
|
||||
"event",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("CREATED", "Created"),
|
||||
("SUBMITTED", "Submitted"),
|
||||
("PROCESSED", "Processed"),
|
||||
("FINISHED", "Finished"),
|
||||
("MAX_PROMPT_METRIC_CHOICES", "Max Prompt Metric Choices"),
|
||||
],
|
||||
default="CREATED",
|
||||
max_length=26,
|
||||
),
|
||||
),
|
||||
("start_time", models.DateTimeField()),
|
||||
("end_time", models.DateTimeField()),
|
||||
(
|
||||
"prompt_length",
|
||||
models.IntegerField(
|
||||
help_text="How many characters are in the prompt"
|
||||
),
|
||||
),
|
||||
(
|
||||
"reponse_legnth",
|
||||
models.IntegerField(
|
||||
help_text="How many characters are in the response"
|
||||
),
|
||||
),
|
||||
("has_file", models.BooleanField(help_text="Is there a file")),
|
||||
(
|
||||
"file_type",
|
||||
models.CharField(help_text="The file type, if any", max_length=16),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="feedback",
|
||||
name="category",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("NOT_DEFINED", "Not defined"),
|
||||
("BUG", "Bug"),
|
||||
("ENHANCEMENT", "Enhancement"),
|
||||
("OTHER", "Other"),
|
||||
("MAX_CATEGORIES", "Max Categories"),
|
||||
],
|
||||
default="NOT_DEFINED",
|
||||
max_length=24,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,54 @@
|
||||
# Generated by Django 4.2.13 on 2024-12-30 20:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0016_promptmetric_alter_feedback_category"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="promptmetric",
|
||||
name="reponse_legnth",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="promptmetric",
|
||||
name="conversation_id",
|
||||
field=models.IntegerField(
|
||||
default=0, help_text="The id of the conversation this matches to"
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="promptmetric",
|
||||
name="model_name",
|
||||
field=models.CharField(
|
||||
default="temp", help_text="The name of the model", max_length=215
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="promptmetric",
|
||||
name="reponse_length",
|
||||
field=models.IntegerField(
|
||||
blank=True,
|
||||
help_text="How many characters are in the response",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="promptmetric",
|
||||
name="end_time",
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="promptmetric",
|
||||
name="file_type",
|
||||
field=models.CharField(
|
||||
blank=True, help_text="The file type, if any", max_length=16, null=True
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.2.17 on 2024-12-31 16:01
|
||||
|
||||
import django.core.files.storage
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0017_remove_promptmetric_reponse_legnth_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="prompt",
|
||||
name="file",
|
||||
field=models.FileField(
|
||||
blank=True,
|
||||
help_text="file for the prompt",
|
||||
null=True,
|
||||
upload_to=django.core.files.storage.FileSystemStorage(
|
||||
location="prompt_files"
|
||||
),
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="prompt",
|
||||
name="file_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
help_text="file type of the file for the prompt",
|
||||
max_length=16,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 4.2.17 on 2025-02-17 15:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("chat_backend", "0018_prompt_file_prompt_file_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="customuser",
|
||||
name="conversation_order",
|
||||
field=models.BooleanField(
|
||||
default=True, help_text="How the conversations should display"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="conversation",
|
||||
name="deleted",
|
||||
field=models.BooleanField(
|
||||
default=False, help_text="This is to hide conversations"
|
||||
),
|
||||
),
|
||||
]
|
||||
0
llm_be/chat_backend/migrations/__init__.py
Normal file
0
llm_be/chat_backend/migrations/__init__.py
Normal file
193
llm_be/chat_backend/models.py
Normal file
193
llm_be/chat_backend/models.py
Normal file
@@ -0,0 +1,193 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.utils import timezone
|
||||
from autoslug import AutoSlugField
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
# Create your models here.
|
||||
|
||||
FILE_STORAGE = FileSystemStorage(location='prompt_files')
|
||||
|
||||
class TimeInfoBase(models.Model):
|
||||
|
||||
created = models.DateTimeField(default=timezone.now)
|
||||
last_modified = models.DateTimeField(default=timezone.now)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not kwargs.pop("skip_last_modified", False) and not hasattr(
|
||||
self, "skip_last_modified"
|
||||
):
|
||||
self.last_modified = timezone.now()
|
||||
if kwargs.get("update_fields") is not None:
|
||||
kwargs["update_fields"] = list(
|
||||
{*kwargs["update_fields"], "last_modified"}
|
||||
)
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class LLMModels(TimeInfoBase):
|
||||
name = models.CharField(max_length=254, default="")
|
||||
port = models.IntegerField(help_text="This specifies the port that the LLM runs on")
|
||||
description = models.CharField(
|
||||
max_length=512,
|
||||
default="",
|
||||
help_text="A description for the LLM. Limit is 512 characters",
|
||||
)
|
||||
|
||||
|
||||
class Company(TimeInfoBase):
|
||||
name = models.TextField(max_length=256)
|
||||
state = models.TextField(max_length=2)
|
||||
zipcode = models.TextField(max_length=5)
|
||||
address = models.TextField(max_length=256)
|
||||
available_llms = models.ForeignKey(
|
||||
"LLMModels",
|
||||
on_delete=models.CASCADE,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text="A list of LLMs that company can use",
|
||||
)
|
||||
|
||||
|
||||
class CustomUser(AbstractUser):
|
||||
company = models.ForeignKey(
|
||||
Company, on_delete=models.CASCADE, blank=True, null=True
|
||||
)
|
||||
is_company_manager = models.BooleanField(
|
||||
help_text="Allows the edit/add/remove of users for a company", default=False
|
||||
)
|
||||
deleted = models.BooleanField(help_text="This is to hid accounts", default=False)
|
||||
has_signed_tos = models.BooleanField(default=False, help_text="If the user has signed the TOS")
|
||||
slug = AutoSlugField(populate_from='email')
|
||||
conversation_order = models.BooleanField(default=True, help_text='How the conversations should display')
|
||||
def get_set_password_url(self):
|
||||
return f"https://www.chat.aimloperations.com/set_password?slug={self.slug}"
|
||||
|
||||
FEEDBACK_CHOICE = (
|
||||
("SUBMITTED", "Submitted"),
|
||||
("RESOLVED", "Resolved"),
|
||||
("DEFFERED", "Deffered"),
|
||||
("CLOSED", "Closed"),
|
||||
)
|
||||
|
||||
FEEDBACK_CATEGORIES = (
|
||||
('NOT_DEFINED', 'Not defined'),
|
||||
('BUG', 'Bug'),
|
||||
('ENHANCEMENT', 'Enhancement'),
|
||||
('OTHER', 'Other'),
|
||||
('MAX_CATEGORIES', 'Max Categories'),
|
||||
)
|
||||
|
||||
class Feedback(TimeInfoBase):
|
||||
title = models.TextField(max_length=64, default='')
|
||||
user = models.ForeignKey(
|
||||
CustomUser, on_delete=models.CASCADE, blank=True, null=True
|
||||
)
|
||||
text = models.TextField(max_length=512)
|
||||
status = models.CharField(max_length=24, choices=FEEDBACK_CHOICE, default="SUBMITTED")
|
||||
category = models.CharField(max_length=24, choices=FEEDBACK_CATEGORIES, default="NOT_DEFINED")
|
||||
|
||||
def get_user_email(self):
|
||||
if self.user:
|
||||
return self.user.email
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
MONTH_CHOICES = (
|
||||
("JANUARY", "January"),
|
||||
("FEBRUARY", "February"),
|
||||
("MARCH", "March"),
|
||||
# ....
|
||||
("DECEMBER", "December"),
|
||||
)
|
||||
|
||||
month = models.CharField(max_length=9,
|
||||
choices=MONTH_CHOICES,
|
||||
default="JANUARY")
|
||||
|
||||
class Announcement(TimeInfoBase):
|
||||
class Status(models.TextChoices):
|
||||
default = "DEFAULT", "default"
|
||||
warning = "WARNING", "warning"
|
||||
info = "INFO", "info"
|
||||
danger = "DANGER", "danger"
|
||||
|
||||
status = models.CharField(
|
||||
max_length=7, choices=Status.choices, default=Status.default
|
||||
)
|
||||
message = models.TextField(max_length=256)
|
||||
start_date_time = models.DateTimeField(auto_now=True)
|
||||
end_date_time = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
class Conversation(TimeInfoBase):
|
||||
user = models.ForeignKey(
|
||||
CustomUser, on_delete=models.CASCADE, blank=True, null=True
|
||||
)
|
||||
title = models.CharField(
|
||||
max_length=64, help_text="The title for the conversation", default=""
|
||||
)
|
||||
deleted = models.BooleanField(help_text="This is to hide conversations", default=False)
|
||||
|
||||
def get_user_email(self):
|
||||
if self.user:
|
||||
return self.user.email
|
||||
else:
|
||||
return ""
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Prompt(TimeInfoBase):
|
||||
message = models.CharField(max_length=10 * 1024, help_text="The text for a prompt")
|
||||
user_created = models.BooleanField(
|
||||
help_text="True if was created by the user. False if it was generate by the LLM"
|
||||
)
|
||||
conversation = models.ForeignKey(
|
||||
"Conversation", on_delete=models.CASCADE, blank=True, null=True
|
||||
)
|
||||
file =models.FileField(upload_to=FILE_STORAGE, blank=True, null=True, help_text="file for the prompt")
|
||||
file_type=models.CharField(max_length=16, blank=True, null=True, help_text='file type of the file for the prompt')
|
||||
|
||||
def get_conversation_title(self):
|
||||
if self.conversation:
|
||||
return self.conversation.title
|
||||
else:
|
||||
return ""
|
||||
|
||||
def file_exists(self):
|
||||
return self.file != None and self.file.storage.exists(self.file.name)
|
||||
|
||||
|
||||
|
||||
class PromptMetric(TimeInfoBase):
|
||||
PROMPT_METRIC_CHOICES = (
|
||||
("CREATED", "Created"),
|
||||
("SUBMITTED", "Submitted"),
|
||||
("PROCESSED", "Processed"),
|
||||
("FINISHED", "Finished"),
|
||||
("MAX_PROMPT_METRIC_CHOICES", "Max Prompt Metric Choices"),
|
||||
)
|
||||
prompt_id = models.IntegerField(help_text="The id of the prompt this matches to")
|
||||
conversation_id = models.IntegerField(help_text="The id of the conversation this matches to")
|
||||
event = models.CharField(
|
||||
max_length=26, choices=PROMPT_METRIC_CHOICES, default='CREATED'
|
||||
)
|
||||
model_name = models.CharField(max_length=215, help_text="The name of the model")
|
||||
start_time = models.DateTimeField()
|
||||
end_time = models.DateTimeField(blank=True, null=True)
|
||||
prompt_length = models.IntegerField( help_text="How many characters are in the prompt")
|
||||
reponse_length = models.IntegerField(blank=True, null=True, help_text="How many characters are in the response")
|
||||
has_file = models.BooleanField(help_text="Is there a file")
|
||||
file_type = models.CharField(max_length=16, help_text='The file type, if any', blank=True, null=True)
|
||||
|
||||
def get_duration(self):
|
||||
if(self.start_time and self.end_time):
|
||||
difference =self.end_time - self.start_time
|
||||
return difference.seconds
|
||||
return 0
|
||||
8
llm_be/chat_backend/renderers.py
Normal file
8
llm_be/chat_backend/renderers.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from rest_framework.renderers import BaseRenderer
|
||||
|
||||
class ServerSentEventRenderer(BaseRenderer):
|
||||
media_type = 'text/event-stream'
|
||||
format = 'txt'
|
||||
|
||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||
return data
|
||||
7
llm_be/chat_backend/routing.py
Normal file
7
llm_be/chat_backend/routing.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.urls import re_path
|
||||
from .views import ChatConsumerAgain
|
||||
|
||||
websocket_urlpatterns = [
|
||||
re_path(r'ws/chat_again/$', ChatConsumerAgain.as_asgi()),
|
||||
|
||||
]
|
||||
69
llm_be/chat_backend/serializers.py
Normal file
69
llm_be/chat_backend/serializers.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
||||
from rest_framework import serializers
|
||||
from .models import CustomUser, Announcement, Company, Conversation, Prompt, Feedback, FEEDBACK_CATEGORIES
|
||||
|
||||
|
||||
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
|
||||
@classmethod
|
||||
def get_token(cls, user):
|
||||
token = super(MyTokenObtainPairSerializer, cls).get_token(user)
|
||||
|
||||
# add custom claim
|
||||
token["company"] = "something here"
|
||||
|
||||
return token
|
||||
|
||||
|
||||
class CompanySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Company
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class AnnouncmentSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Announcement
|
||||
fields = "__all__"
|
||||
|
||||
class FeedbackSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Feedback
|
||||
fields = "__all__"
|
||||
|
||||
class CustomUserSerializer(serializers.ModelSerializer):
|
||||
email = serializers.EmailField(required=True)
|
||||
username = serializers.CharField()
|
||||
password = serializers.CharField(min_length=8, write_only=True)
|
||||
company = CompanySerializer()
|
||||
has_usable_password = serializers.BooleanField()
|
||||
|
||||
class Meta:
|
||||
model = CustomUser
|
||||
fields = "__all__"
|
||||
extra_kwargs = {"password": {"write_only": True}}
|
||||
|
||||
# def create(self, validated_data):
|
||||
# password = validated_data.pop('password',None)
|
||||
# instance = self.Meta.model(**validated_data)
|
||||
# if password is not None:
|
||||
# instance.set_password(password)
|
||||
# instance.save()
|
||||
# return instance
|
||||
|
||||
|
||||
class ConversationSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Conversation
|
||||
fields = ("title", "created", "last_modified", "id")
|
||||
|
||||
|
||||
class PromptSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Prompt
|
||||
fields = ("message", "user_created", "created", "id", )
|
||||
|
||||
class BasicUserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = CustomUser
|
||||
fields = ("email", "first_name", "last_name", "is_active","has_usable_password","is_company_manager",'has_signed_tos')
|
||||
105
llm_be/chat_backend/templates/emails/feedback_email.html
Normal file
105
llm_be/chat_backend/templates/emails/feedback_email.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>New Feedback Submission</title>
|
||||
<style>
|
||||
/* Basic reset for email clients */
|
||||
body, table, td, a {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
table, td {
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
.header {
|
||||
background-color: #007BFF;
|
||||
color: #ffffff;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
color: #333333;
|
||||
}
|
||||
.footer {
|
||||
background-color: #f4f4f4;
|
||||
color: #777777;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.feedback-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.feedback-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<!-- Email Container -->
|
||||
<div class="email-container">
|
||||
<!-- Header -->
|
||||
<div class="header">
|
||||
<h1>New Feedback Submission</h1>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="content">
|
||||
<p>Hello,</p>
|
||||
<p>A new feedback item has been submitted. Here are the details:</p>
|
||||
|
||||
<!-- Feedback Title -->
|
||||
<div class="feedback-title">
|
||||
Title: <strong>{{ title }}</strong>
|
||||
</div>
|
||||
|
||||
<!-- Feedback Text -->
|
||||
<div class="feedback-text">
|
||||
<strong>Feedback:</strong><br>
|
||||
{{ feedback_text }}
|
||||
</div>
|
||||
|
||||
<p>Thank you for your attention.</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<p>This is an automated message. Please do not reply to this email.</p>
|
||||
<p>© 2025 AI ML Operations, LLC. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
3
llm_be/chat_backend/templates/emails/feedback_email.txt
Normal file
3
llm_be/chat_backend/templates/emails/feedback_email.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
New feedback for Chat by AI ML Operations, LLC
|
||||
|
||||
"New Feedback. {{ title }}. {{ feedback_text }}"
|
||||
97
llm_be/chat_backend/templates/emails/invite_email.html
Normal file
97
llm_be/chat_backend/templates/emails/invite_email.html
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Invitation to Chat by AI ML Operations, LLC</title>
|
||||
<style>
|
||||
/* Basic reset for email clients */
|
||||
body, table, td, a {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
table, td {
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
.header {
|
||||
background-color: #007BFF;
|
||||
color: #ffffff;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
color: #333333;
|
||||
}
|
||||
.footer {
|
||||
background-color: #f4f4f4;
|
||||
color: #777777;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.feedback-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.feedback-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<!-- Email Container -->
|
||||
<div class="email-container">
|
||||
<!-- Header -->
|
||||
<div class="header">
|
||||
<h1>Welcome to AI ML Operations, LLC Chat Services</h1>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="content">
|
||||
<p>Hello,</p>
|
||||
<p>You have been invited to use Chat by AI ML Operations, LLC.</p>
|
||||
|
||||
<p>Please click <a href="{{ url }}">link</a> to set your password.</p>
|
||||
<p>Once you have set your password go <a href="https://chat.aimloperations.com">here</a> to get started.</p>
|
||||
|
||||
<p>Thank you.</p>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="footer">
|
||||
<p>This is an automated message. Please do not reply to this email.</p>
|
||||
<p>© 2025 AI ML Operations, LLC. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
3
llm_be/chat_backend/templates/emails/invite_email.txt
Normal file
3
llm_be/chat_backend/templates/emails/invite_email.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Welcome to AI ML Operations, LLC Chat Services
|
||||
|
||||
"Welcome to chat.aimloperations.com. Please use {{ url }} to set your password"
|
||||
3
llm_be/chat_backend/tests.py
Normal file
3
llm_be/chat_backend/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
52
llm_be/chat_backend/urls.py
Normal file
52
llm_be/chat_backend/urls.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from django.urls import path
|
||||
from rest_framework_simplejwt import views as jwt_views
|
||||
from .views import (
|
||||
AcknowledgeTermsOfService,
|
||||
CustomObtainTokenView,
|
||||
CustomUserCreate,
|
||||
CustomUserInvite,
|
||||
LogoutAndBlacklistRefreshTokenForUserView,
|
||||
CustomUserGet,
|
||||
is_authenticated,
|
||||
AnnouncmentView,
|
||||
FeedbackView,
|
||||
ConversationsView,
|
||||
ConversationDetailView,
|
||||
CompanyUsersView,
|
||||
SetUserPassword,
|
||||
ConversationPreferences,
|
||||
UserPromptAnalytics,
|
||||
UserConversationAnalytics,
|
||||
CompanyUsageAnalytics,
|
||||
AdminAnalytics
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("token/obtain/", CustomObtainTokenView.as_view(), name="token_create"),
|
||||
path("token/refresh/", jwt_views.TokenRefreshView.as_view(), name="token_refresh"),
|
||||
path("user/create/", CustomUserCreate.as_view(), name="create_user"),
|
||||
path("user/invite/", CustomUserInvite.as_view(), name="invite_user"),
|
||||
path("user/set_password/<slug:slug>/", SetUserPassword.as_view(), name="set_password"),
|
||||
path(
|
||||
"blacklist/",
|
||||
LogoutAndBlacklistRefreshTokenForUserView.as_view(),
|
||||
name="blacklist",
|
||||
),
|
||||
path("user/get/", CustomUserGet.as_view(), name="get_user"),
|
||||
path("user/acknowledge_tos/", AcknowledgeTermsOfService.as_view(), name="acknowledge_tos"),
|
||||
path("company_users",CompanyUsersView.as_view(), name="company_users"),
|
||||
path("user/is_authenticated/", is_authenticated, name="is_authenticated"),
|
||||
path("announcment/get/", AnnouncmentView.as_view(), name="get_announcments"),
|
||||
path("conversations", ConversationsView.as_view(), name="conversations"),
|
||||
path("feedbacks/", FeedbackView.as_view(), name="feedbacks"),
|
||||
path(
|
||||
"conversation_details",
|
||||
ConversationDetailView.as_view(),
|
||||
name="conversation_details",
|
||||
),
|
||||
path("conversation_preferences", ConversationPreferences.as_view(), name="conversation_preferences"),
|
||||
path("analytics/user_prompts/", UserPromptAnalytics.as_view(), name="analytics_user_prompts"),
|
||||
path("analytics/user_conversations/", UserConversationAnalytics.as_view(), name="analytics_user_conversations"),
|
||||
path("analytics/company_usage/", CompanyUsageAnalytics.as_view(), name="analytics_company_usage"),
|
||||
path("analytics/admin/", AdminAnalytics.as_view(), name="analytics_admin"),
|
||||
]
|
||||
7
llm_be/chat_backend/utils.py
Normal file
7
llm_be/chat_backend/utils.py
Normal file
@@ -0,0 +1,7 @@
|
||||
import datetime
|
||||
|
||||
def last_day_of_month(any_day):
|
||||
# The day 28 exists in every month. 4 days later, it's always next month
|
||||
next_month = any_day.replace(day=28) + datetime.timedelta(days=4)
|
||||
# subtracting the number of the current day brings us back one month
|
||||
return next_month - datetime.timedelta(days=next_month.day)
|
||||
714
llm_be/chat_backend/views.py
Normal file
714
llm_be/chat_backend/views.py
Normal file
@@ -0,0 +1,714 @@
|
||||
from channels.layers import get_channel_layer
|
||||
from channels.db import database_sync_to_async
|
||||
from rest_framework_simplejwt.views import TokenObtainPairView
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework import permissions, status
|
||||
from .serializers import (
|
||||
MyTokenObtainPairSerializer,
|
||||
CustomUserSerializer,
|
||||
BasicUserSerializer,
|
||||
AnnouncmentSerializer,
|
||||
CompanySerializer,
|
||||
ConversationSerializer,
|
||||
PromptSerializer,
|
||||
FeedbackSerializer
|
||||
)
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from .models import CustomUser, Announcement, Conversation, Prompt, Feedback,PromptMetric
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.http import JsonResponse
|
||||
from datetime import datetime
|
||||
from .client import LlamaClient
|
||||
from asgiref.sync import sync_to_async, async_to_sync
|
||||
from channels.generic.websocket import AsyncWebsocketConsumer
|
||||
from langchain_ollama.llms import OllamaLLM
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
import re
|
||||
from django.conf import settings
|
||||
import json
|
||||
import base64
|
||||
import pandas as pd
|
||||
|
||||
# For email support
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
from django.template.loader import get_template
|
||||
from django.template import Context
|
||||
from django.utils import timezone
|
||||
from django.core.files import File
|
||||
from django.core.files.base import ContentFile
|
||||
import math
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from .utils import last_day_of_month
|
||||
|
||||
|
||||
CHANNEL_NAME: str = 'llm_messages'
|
||||
MODEL_NAME: str = "llama3"
|
||||
|
||||
# Create your views here.
|
||||
class CustomObtainTokenView(TokenObtainPairView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
serializer_class = MyTokenObtainPairSerializer
|
||||
|
||||
|
||||
class CustomUserCreate(APIView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = ()
|
||||
|
||||
def post(self, request, format="json"):
|
||||
serializer = CustomUserSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
user = serializer.save()
|
||||
if user:
|
||||
json = serializer.data
|
||||
return Response(json, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def send_invite_email(slug, email_to_invite):
|
||||
print(f"url : https://www.chat.aimloperations.com/set_password?slug={slug}")
|
||||
url = f"https://www.chat.aimloperations.com/set_password?slug={slug}"
|
||||
subject = "Welcome to AI ML Operations, LLC Chat Services"
|
||||
from_email = "ryan@aimloperations.com"
|
||||
to=email_to_invite
|
||||
d = {"url": url}
|
||||
html_content = get_template(r'emails/invite_email.html').render(d)
|
||||
text_content = get_template(r'emails/invite_email.txt').render(d)
|
||||
|
||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||
msg.attach_alternative(html_content, "text/html")
|
||||
msg.send(fail_silently=True)
|
||||
|
||||
def send_feedback_email(feedback_obj):
|
||||
subject = "New Feedback for Chat by AI ML Operations, LLC"
|
||||
from_email = "ryan@aimloperations.com"
|
||||
to="ryan@aimloperations.com"
|
||||
d = {"title": feedback_obj.title, "feedback_text": feedback_obj.text}
|
||||
html_content = get_template(r'emails/feedback_email.html').render(d)
|
||||
text_content = get_template(r'emails/feedback_email.txt').render(d)
|
||||
|
||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||
msg.attach_alternative(html_content, "text/html")
|
||||
msg.send(fail_silently=True)
|
||||
|
||||
class CustomUserInvite(APIView):
|
||||
http_method_names = ['post']
|
||||
def post(self, request, format="json"):
|
||||
def valid_email(email_string):
|
||||
regex = r'^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w+$'
|
||||
if re.match(regex,email_string):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
email_to_invite = request.data['email']
|
||||
|
||||
if len(email_to_invite) == 0 or not valid_email(email_to_invite) or not request.user.is_company_manager:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
# make sure there isn't a user with this email already
|
||||
existing_users = CustomUser.objects.filter(email=email_to_invite)
|
||||
if len(existing_users) > 0:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
# create the object and send the email
|
||||
user = CustomUser.objects.create(email=email_to_invite, username=email_to_invite, company=request.user.company)
|
||||
|
||||
# send an email
|
||||
send_invite_email(user.slug, email_to_invite)
|
||||
|
||||
|
||||
|
||||
return Response(status=status.HTTP_201_CREATED)
|
||||
|
||||
class SetUserPassword(APIView):
|
||||
http_method_names = ['post','get']
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = ()
|
||||
def get(self, request, slug):
|
||||
user = CustomUser.objects.get(slug=slug)
|
||||
if user.last_login:
|
||||
return Response(status=status.HTTP_401_UNAUTHORIZED)
|
||||
else:
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
def post(self, request, slug, format="json"):
|
||||
user = CustomUser.objects.get(slug=slug)
|
||||
user.set_password(request.data['password'])
|
||||
user.save()
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
|
||||
class CustomUserGet(APIView):
|
||||
http_method_names = ['get', 'head', 'post']
|
||||
def get(self, request, format="json"):
|
||||
|
||||
email = request.user.email
|
||||
username = request.user.username
|
||||
user = CustomUser.objects.get(email=email)
|
||||
serializer = CustomUserSerializer(user)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
class FeedbackView(APIView):
|
||||
http_method_names = ['post','get']
|
||||
def post(self, request, format="json"):
|
||||
serializer = FeedbackSerializer(data=request.data)
|
||||
print(request.data)
|
||||
if serializer.is_valid():
|
||||
|
||||
feedback_obj = serializer.save()
|
||||
feedback_obj.user = request.user
|
||||
|
||||
feedback_obj.save()
|
||||
send_feedback_email(feedback_obj)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
else:
|
||||
print(serializer.errors)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def get(self, request, format="json"):
|
||||
feedback_objs = Feedback.objects.filter(user=request.user)
|
||||
serializer = FeedbackSerializer(feedback_objs, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
|
||||
class AcknowledgeTermsOfService(APIView):
|
||||
http_method_names = ['post']
|
||||
def post(self, request, format="json"):
|
||||
request.user.has_signed_tos = True
|
||||
request.user.save()
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
class CompanyUsersView(APIView):
|
||||
def get(self, request, format="json"):
|
||||
# TODO: make sure you are a manager of that company
|
||||
if request.user.is_company_manager:
|
||||
users = CustomUser.objects.filter(company_id=request.user.company.id)
|
||||
serializer = BasicUserSerializer(users, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
else:
|
||||
return Response(status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
|
||||
def post(self, request, format="json"):
|
||||
if request.user.is_company_manager:
|
||||
user = CustomUser.objects.get(email=request.data.get("email"))
|
||||
if request.user.company_id == user.company_id:
|
||||
field = request.data.get("field")
|
||||
data = {}
|
||||
if field == "is_active":
|
||||
data.update({"is_active": not user.is_active})
|
||||
elif field == "company_manager":
|
||||
data.update({"is_company_manager": not user.is_company_manager})
|
||||
elif field == "has_password":
|
||||
if user.has_usable_password():
|
||||
user.set_unusable_password()
|
||||
serializer = CustomUserSerializer(user, data, partial=True)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
def delete(self, request, format="json"):
|
||||
if request.user.is_company_manager:
|
||||
user = CustomUser.objects.get(email=request.data.get("email"))
|
||||
if request.user.company_id == user.company_id:
|
||||
user.delete()
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
return Response(status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
class AnnouncmentView(APIView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
serializer_class = AnnouncmentSerializer
|
||||
|
||||
def get(self, request, format="json"):
|
||||
announcements = Announcement.objects.all()
|
||||
serializer = AnnouncmentSerializer(announcements, many=True)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class LogoutAndBlacklistRefreshTokenForUserView(APIView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = ()
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
refresh_token = request.data["refresh_token"]
|
||||
token = RefreshToken(refresh_token)
|
||||
token.blacklist()
|
||||
return Response(status=status.HTTP_205_RESET_CONTENT)
|
||||
except Exception as e:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@never_cache
|
||||
def is_authenticated(request):
|
||||
if request.user.is_authenticated:
|
||||
return JsonResponse({}, status=status.HTTP_200_OK)
|
||||
return JsonResponse({}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
|
||||
class ConversationsView(APIView):
|
||||
def get(self, request, format="json"):
|
||||
order = "created" if request.user.conversation_order else "-created"
|
||||
conversations = Conversation.objects.filter(user=request.user, deleted=False).order_by(order)
|
||||
serializer = ConversationSerializer(conversations, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
def post(self, request, format="json"):
|
||||
"""
|
||||
Create a blank conversation and return the title and id number
|
||||
"""
|
||||
title = request.data.get("name")
|
||||
conversation = Conversation.objects.create(title=title)
|
||||
conversation.save()
|
||||
conversation.user_id = request.user.id
|
||||
conversation.save()
|
||||
|
||||
# TODO: when we are smart enough to create a conversation when a prompt is sent
|
||||
# client = LlamaClient()
|
||||
# inital_message = request.data.get("prompt")
|
||||
# title = client.generate_conversation_title(inital_message)
|
||||
# title = title if title else "New Conversation"
|
||||
# conversation = Conversation.objects.create(title=title)
|
||||
# conversation.save()
|
||||
# conversation.user_id = request.user.id
|
||||
# conversation.save()
|
||||
|
||||
return Response({"title": title, "id": conversation.id}, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class ConversationPreferences(APIView):
|
||||
def get(self, request, format="json"):
|
||||
user = request.user
|
||||
return Response({"order": user.conversation_order}, status=status.HTTP_200_OK)
|
||||
|
||||
def post(self, request, format="json"):
|
||||
user = request.user
|
||||
user.conversation_order = not user.conversation_order
|
||||
user.save()
|
||||
return Response({"order": user.conversation_order}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
|
||||
class ConversationDetailView(APIView):
|
||||
def get(self, request, format="json"):
|
||||
conversation_id = request.query_params.get("conversation_id")
|
||||
prompts = Prompt.objects.filter(conversation__id=conversation_id)
|
||||
serailzer = PromptSerializer(prompts, many=True)
|
||||
return Response(serailzer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
def post(self, request, format="json"):
|
||||
print('In the post')
|
||||
# Add the prompt to the database
|
||||
# make sure there is a conversation for it
|
||||
# if there is not a conversation create a title for it
|
||||
|
||||
# make sure that our model exists and it is running
|
||||
prompt = request.data.get("prompt")
|
||||
|
||||
conversation_id = request.data.get("conversation_id")
|
||||
is_user = bool(request.data.get("is_user"))
|
||||
|
||||
try:
|
||||
conversation = Conversation.objects.get(id=conversation_id)
|
||||
|
||||
# add the prompt to the conversation
|
||||
serializer = PromptSerializer(
|
||||
data={
|
||||
"message": prompt,
|
||||
"user_created": is_user,
|
||||
"created": datetime.now(),
|
||||
}
|
||||
)
|
||||
if serializer.is_valid():
|
||||
prompt_instance = serializer.save()
|
||||
prompt_instance.conversation_id = conversation.id
|
||||
prompt_instance = serializer.save()
|
||||
|
||||
# set up the streaming response if it is from the user
|
||||
print(f'Do we have a valid user? {is_user}')
|
||||
if is_user:
|
||||
messages = []
|
||||
for prompt_obj in Prompt.objects.filter(conversation__id=conversation_id):
|
||||
messages.append({
|
||||
'content':prompt_obj.message,
|
||||
'role': 'user' if prompt_obj.user_created else 'assistant'
|
||||
})
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
print(f'Sending to the channel: {CHANNEL_NAME}')
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
CHANNEL_NAME, {
|
||||
'type':'receive',
|
||||
'content': messages
|
||||
}
|
||||
)
|
||||
except:
|
||||
print(f"Error trying to submit to conversation_id: {conversation_id} with request.data: {request.data}")
|
||||
pass
|
||||
|
||||
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
def delete(self, request, format="json"):
|
||||
conversation_id = request.data.get("conversation_id")
|
||||
conversation = Conversation.objects.get(id=conversation_id, user=request.user)
|
||||
conversation.deleted = True
|
||||
conversation.save()
|
||||
return Response(status=status.HTTP_202_ACCEPTED)
|
||||
|
||||
class UserPromptAnalytics(APIView):
|
||||
def get(self, request, format="json"):
|
||||
now = timezone.now()
|
||||
result = []
|
||||
|
||||
number_of_months = 3
|
||||
company_user_ids = CustomUser.objects.filter(company=request.user.company).values_list('id', flat=True)
|
||||
for i in range(number_of_months):
|
||||
next_year = now.year
|
||||
next_month = now.month - i
|
||||
while next_month < 1:
|
||||
next_year -= 1
|
||||
next_month += 12
|
||||
|
||||
start_date = datetime.datetime(next_year, next_month, 1)
|
||||
end_date = last_day_of_month(start_date)
|
||||
total_conversations = Conversation.objects.filter(created__gte=start_date, created__lte=end_date)
|
||||
total_prompts = Prompt.objects.filter(conversation__id__in=total_conversations, created__gte=start_date, created__lte=end_date)
|
||||
total_users = len(CustomUser.objects.all())
|
||||
my_conversations = Conversation.objects.filter(user=request.user)
|
||||
my_prompts = Prompt.objects.filter(conversation__in=my_conversations, created__gte=start_date, created__lte=end_date)
|
||||
company_conversations = Conversation.objects.filter(user__id__in=company_user_ids)
|
||||
company_prompts = Prompt.objects.filter(conversation__in=company_conversations, created__gte=start_date, created__lte=end_date)
|
||||
|
||||
result.append({
|
||||
"month":start_date.strftime("%B"),
|
||||
"you": len(my_prompts),
|
||||
"others": len(company_prompts)/len(company_user_ids),
|
||||
"all":len(total_prompts)/total_users
|
||||
})
|
||||
|
||||
return Response(result[::-1], status=status.HTTP_200_OK)
|
||||
|
||||
class UserConversationAnalytics(APIView):
|
||||
def get(self, request, format="json"):
|
||||
now = timezone.now()
|
||||
result = []
|
||||
|
||||
number_of_months = 3
|
||||
company_user_ids = CustomUser.objects.filter(company=request.user.company).values_list('id', flat=True)
|
||||
for i in range(number_of_months):
|
||||
next_year = now.year
|
||||
next_month = now.month - i
|
||||
while next_month < 1:
|
||||
next_year -= 1
|
||||
next_month += 12
|
||||
|
||||
start_date = datetime.datetime(next_year, next_month, 1)
|
||||
end_date = last_day_of_month(start_date)
|
||||
total_conversations = len(Conversation.objects.filter(created__gte=start_date, created__lte=end_date))
|
||||
total_users = len(CustomUser.objects.all())
|
||||
company_conversations = len(Conversation.objects.filter(user__id__in=company_user_ids, created__gte=start_date, created__lte=end_date))
|
||||
|
||||
result.append({
|
||||
"month":start_date.strftime("%B"),
|
||||
"you": len(Conversation.objects.filter(user=request.user, created__gte=start_date, created__lte=end_date)),
|
||||
"others": company_conversations/len(company_user_ids),
|
||||
"all":total_conversations/total_users
|
||||
})
|
||||
|
||||
|
||||
return Response(result[::-1], status=status.HTTP_200_OK)
|
||||
|
||||
class CompanyUsageAnalytics(APIView):
|
||||
def get(self, request, format="json"):
|
||||
now = timezone.now()
|
||||
result = []
|
||||
|
||||
number_of_months = 3
|
||||
company_user_ids = CustomUser.objects.filter(company=request.user.company).values_list('id', flat=True)
|
||||
|
||||
for i in range(number_of_months):
|
||||
next_year = now.year
|
||||
next_month = now.month - i
|
||||
while next_month < 1:
|
||||
next_year -= 1
|
||||
next_month += 12
|
||||
|
||||
start_date = datetime.datetime(next_year, next_month, 1)
|
||||
end_date = last_day_of_month(start_date)
|
||||
conversations = Conversation.objects.filter(user__id__in=company_user_ids, created__gte=start_date, created__lte=end_date)
|
||||
|
||||
conversation_user_ids = conversations.values_list("user__id", flat=True).distinct()
|
||||
result.append({
|
||||
"month":start_date.strftime("%B"),
|
||||
"used":len(conversation_user_ids),
|
||||
"not_used":len(company_user_ids) - len(conversation_user_ids)
|
||||
})
|
||||
return Response(result[::-1], status=status.HTTP_200_OK)
|
||||
|
||||
class AdminAnalytics(APIView):
|
||||
def get(self, request, format="json"):
|
||||
number_of_months = 3
|
||||
result = []
|
||||
now = timezone.now()
|
||||
|
||||
for i in range(number_of_months):
|
||||
next_year = now.year
|
||||
next_month = now.month - i
|
||||
while next_month < 1:
|
||||
next_year -= 1
|
||||
next_month += 12
|
||||
|
||||
start_date = datetime.datetime(next_year, next_month, 1)
|
||||
end_date = last_day_of_month(start_date)
|
||||
durations = [item.get_duration() for item in PromptMetric.objects.filter(created__gte=start_date, created__lte=end_date)]
|
||||
if len(durations) == 0:
|
||||
result.append({
|
||||
"month":start_date.strftime("%B"),
|
||||
"range":[0,0],
|
||||
"avg": 0,
|
||||
"median":0,
|
||||
})
|
||||
continue
|
||||
|
||||
average = sum(durations)/len(durations)
|
||||
min_value = min(durations)
|
||||
max_value = max(durations)
|
||||
durations.sort()
|
||||
median = durations[len(durations)//2]
|
||||
result.append({
|
||||
"month":start_date.strftime("%B"),
|
||||
"range":[min_value,max_value],
|
||||
"avg": average,
|
||||
"median":median,
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
return Response(result[::-1], status=status.HTTP_200_OK)
|
||||
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "You are a helpful assistant."),
|
||||
("user", "{input}")
|
||||
])
|
||||
|
||||
llm = OllamaLLM(model=MODEL_NAME)
|
||||
|
||||
# output_parser = StrOutputParser()
|
||||
# # Chain
|
||||
# chain = prompt | llm.with_config({"run_name": "model"}) | output_parser.with_config({"run_name": "Assistant"})
|
||||
|
||||
@database_sync_to_async
|
||||
def create_conversation(prompt, email):
|
||||
# return the conversation id
|
||||
|
||||
|
||||
response = llm.invoke("Summarise the phrase in one to for words\"%s\"" % prompt)
|
||||
print(f"Response: {response}")
|
||||
print(dir(response))
|
||||
title = response.replace("\"","")
|
||||
title = " ".join(title.split(" ")[:4])
|
||||
|
||||
|
||||
conversation = Conversation.objects.create(title = title)
|
||||
conversation.save()
|
||||
|
||||
user = CustomUser.objects.get(email=email)
|
||||
conversation.user_id = user.id
|
||||
conversation.save()
|
||||
return conversation.id
|
||||
|
||||
@database_sync_to_async
|
||||
def get_messages(conversation_id, prompt, file_string: str = None, file_type: str = ""):
|
||||
messages = []
|
||||
|
||||
conversation = Conversation.objects.get(id=conversation_id)
|
||||
print(file_string)
|
||||
|
||||
# add the prompt to the conversation
|
||||
serializer = PromptSerializer(
|
||||
data={
|
||||
"message": prompt,
|
||||
"user_created": True,
|
||||
"created": datetime.now(),
|
||||
}
|
||||
)
|
||||
if serializer.is_valid(raise_exception=True):
|
||||
prompt_instance = serializer.save()
|
||||
prompt_instance.conversation_id = conversation.id
|
||||
prompt_instance.save()
|
||||
if file_string:
|
||||
file_name = f"prompt_{prompt_instance.id}_data.{file_type}"
|
||||
f = ContentFile(file_string, name=file_name)
|
||||
prompt_instance.file.save(file_name, f)
|
||||
prompt_instance.file_type = file_type
|
||||
prompt_instance.save()
|
||||
|
||||
|
||||
|
||||
for prompt_obj in Prompt.objects.filter(conversation__id=conversation_id):
|
||||
messages.append({
|
||||
'content': prompt_obj.message,
|
||||
'role': 'user' if prompt_obj.user_created else 'assistant',
|
||||
'has_file': prompt_obj.file_exists(),
|
||||
'file': prompt_obj.file if prompt_obj.file_exists() else None,
|
||||
'file_type': prompt_obj.file_type if prompt_obj.file_exists() else None,
|
||||
})
|
||||
|
||||
# now transform the messages
|
||||
transformed_messages = []
|
||||
for message in messages:
|
||||
|
||||
if message['has_file'] and message['file_type'] != None:
|
||||
if 'csv' in message['file_type']:
|
||||
file_type = 'csv'
|
||||
altered_message = f"{message['content']}\n The file type is csv and the file contents are: {message['file'].read()}"
|
||||
elif 'xlsx' in message['file_type']:
|
||||
file_type = 'xlsx'
|
||||
df = pd.read_excel(message['file'].read())
|
||||
altered_message = f"{message['content']}\n The file type is xlsx and the file contents are: {df}"
|
||||
elif 'txt' in message['file_type']:
|
||||
file_type = 'txt'
|
||||
altered_message = f"{message['content']}\n The file type is csv and the file contents are: {message['file'].read()}"
|
||||
else:
|
||||
altered_message = message['content']
|
||||
|
||||
transformed_message = SystemMessage(content=altered_message) if message['role'] == 'assistant' else HumanMessage(content=altered_message)
|
||||
transformed_messages.append(transformed_message)
|
||||
|
||||
return transformed_messages, prompt_instance
|
||||
|
||||
|
||||
@database_sync_to_async
|
||||
def save_generated_message(conversation_id, message):
|
||||
conversation = Conversation.objects.get(id=conversation_id)
|
||||
|
||||
# add the prompt to the conversation
|
||||
serializer = PromptSerializer(
|
||||
data={
|
||||
"message": message,
|
||||
"user_created": False,
|
||||
"created": datetime.now(),
|
||||
}
|
||||
)
|
||||
if serializer.is_valid():
|
||||
prompt_instance = serializer.save()
|
||||
prompt_instance.conversation_id = conversation.id
|
||||
prompt_instance = serializer.save()
|
||||
|
||||
@database_sync_to_async
|
||||
def create_prompt_metric(prompt_id, prompt, has_file, file_type, model_name, conversation_id):
|
||||
prompt_metric = PromptMetric.objects.create(
|
||||
prompt_id=prompt_id,
|
||||
start_time = timezone.now(),
|
||||
prompt_length = len(prompt),
|
||||
has_file = has_file,
|
||||
file_type = file_type,
|
||||
model_name=model_name,
|
||||
conversation_id=conversation_id,
|
||||
)
|
||||
prompt_metric.save()
|
||||
return prompt_metric
|
||||
|
||||
@database_sync_to_async
|
||||
def update_prompt_metric(prompt_metric, status):
|
||||
prompt_metric.event = status
|
||||
prompt_metric.save()
|
||||
|
||||
@database_sync_to_async
|
||||
def finish_prompt_metric(prompt_metric, response_length):
|
||||
print(f'finish_prompt_metric: {response_length}')
|
||||
prompt_metric.end_time = timezone.now()
|
||||
prompt_metric.reponse_length = response_length
|
||||
prompt_metric.event = 'FINISHED'
|
||||
prompt_metric.save(update_fields=["end_time", "reponse_length","event"])
|
||||
print("finish_prompt_metric saved")
|
||||
|
||||
class ChatConsumerAgain(AsyncWebsocketConsumer):
|
||||
async def connect(self):
|
||||
await self.accept()
|
||||
|
||||
async def disconnect(self, close_code):
|
||||
await self.close()
|
||||
|
||||
async def receive(self, text_data=None, bytes_data=None):
|
||||
print(f"Text Data: {text_data}")
|
||||
print(f"Bytes Data: {bytes_data}")
|
||||
if text_data:
|
||||
data = json.loads(text_data)
|
||||
message = data.get('message',None)
|
||||
conversation_id = data.get('conversation_id',None)
|
||||
email = data.get("email", None)
|
||||
file = data.get("file", None)
|
||||
file_type = data.get("fileType", "")
|
||||
|
||||
if not conversation_id:
|
||||
# we need to create a new conversation
|
||||
# we will generate a name for it too
|
||||
conversation_id = await create_conversation(message, email)
|
||||
|
||||
if conversation_id:
|
||||
decoded_file = None
|
||||
|
||||
if file:
|
||||
decoded_file = base64.b64decode(file)
|
||||
print(decoded_file)
|
||||
if 'csv' in file_type:
|
||||
file_type = 'csv'
|
||||
altered_message = f"{message}\n The file type is csv and the file contents are: {decoded_file}"
|
||||
elif 'xmlformats-officedocument' in file_type:
|
||||
file_type = 'xlsx'
|
||||
df = pd.read_excel(decoded_file)
|
||||
altered_message = f"{message}\n The file type is xlsx and the file contents are: {df}"
|
||||
elif 'text' in file_type:
|
||||
file_type = 'txt'
|
||||
altered_message = f"{message}\n The file type is txt and the file contents are: {decoded_file}"
|
||||
else:
|
||||
file_type = 'Not Sure'
|
||||
|
||||
|
||||
|
||||
|
||||
print(f'received: "{message}" for conversation {conversation_id}')
|
||||
# TODO: add the message to the database
|
||||
# get the new conversation
|
||||
# TODO: get the messages here
|
||||
|
||||
messages, prompt = await get_messages(conversation_id, message, decoded_file, file_type)
|
||||
|
||||
prompt_metric = await create_prompt_metric(prompt.id, prompt.message, True if file else False, file_type, MODEL_NAME, conversation_id)
|
||||
if file:
|
||||
# udpate with the altered_message
|
||||
messages = messages[:-1] + [HumanMessage(content=altered_message)]
|
||||
print(messages)
|
||||
# send it to the LLM
|
||||
|
||||
# stream the response back
|
||||
response = ""
|
||||
# start of the message
|
||||
await self.send('CONVERSATION_ID')
|
||||
await self.send(str(conversation_id))
|
||||
await self.send('START_OF_THE_STREAM_ENDER_GAME_42')
|
||||
async for chunk in llm.astream(messages):
|
||||
print(f"chunk: {chunk}")
|
||||
response += chunk
|
||||
await self.send(chunk)
|
||||
await self.send('END_OF_THE_STREAM_ENDER_GAME_42')
|
||||
await save_generated_message(conversation_id, response)
|
||||
|
||||
await finish_prompt_metric(prompt_metric, len(response))
|
||||
|
||||
if bytes_data:
|
||||
print("we have byte data")
|
||||
0
llm_be/llm_be/__init__.py
Normal file
0
llm_be/llm_be/__init__.py
Normal file
25
llm_be/llm_be/asgi.py
Normal file
25
llm_be/llm_be/asgi.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
ASGI config for llm_be 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/3.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from channels.auth import AuthMiddlewareStack
|
||||
import chat_backend.routing
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'llm_be.settings')
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
"http": get_asgi_application(),
|
||||
"websocket": AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
chat_backend.routing.websocket_urlpatterns
|
||||
)
|
||||
),
|
||||
})
|
||||
205
llm_be/llm_be/settings.py
Normal file
205
llm_be/llm_be/settings.py
Normal file
@@ -0,0 +1,205 @@
|
||||
"""
|
||||
Django settings for llm_be project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.2.18.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import timedelta
|
||||
import os
|
||||
|
||||
# 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/3.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-6suk6fj5q2)1tj%)f(wgw1smnliv5-#&@zvgvj1wp#(#@h#31x'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['*.aimloperations.com','localhost','127.0.0.1','chat.aimloperations.com','chatbackend.aimloperations.com']
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'daphne',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'chat_backend',
|
||||
'rest_framework',
|
||||
'corsheaders',
|
||||
'rest_framework_simplejwt.token_blacklist',
|
||||
|
||||
]
|
||||
|
||||
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',
|
||||
"corsheaders.middleware.CorsMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'llm_be.urls'
|
||||
|
||||
# SETTINGS_PATH = os.path.dirname(os.path.dirname(__file__))
|
||||
# TEMPLATE_DIRS = (
|
||||
# os.path.join(SETTINGS_PATH, 'templates'),
|
||||
# )
|
||||
|
||||
print(os.path.join(BASE_DIR, 'templates'))
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
||||
'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 = 'llm_be.wsgi.application'
|
||||
ASGI_APPLICATION = 'llm_be.asgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.2/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/3.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# custom user model
|
||||
AUTH_USER_MODEL = 'chat_backend.CustomUser'
|
||||
|
||||
# rest framework jwt stuff
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'rest_framework.permissions.IsAuthenticated',
|
||||
),
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
), #
|
||||
}
|
||||
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME':timedelta(hours=5),
|
||||
'REFRESH_TOKEN_LIFETIME':timedelta(days=14),
|
||||
'ROTATE_REFRESH_TOKENS':True,
|
||||
'BLACKLIST_AFTER_ROTATION':True,
|
||||
'ALGORITHM':"HS256",
|
||||
"SIGNING_KEY":SECRET_KEY,
|
||||
'VERIFYING_KEY':None,
|
||||
"AUTH_HEADER_TYPES":('JWT',),
|
||||
'USER_ID_FIELD':'id',
|
||||
'USER_ID_CLAIM':'user_id',
|
||||
'AUTH_TOKEN_CLASSES':('rest_framework_simplejwt.tokens.AccessToken',),
|
||||
'TOKEN_TYPE_CLAIM':'token_type',
|
||||
}
|
||||
|
||||
# CORS settings
|
||||
CORS_ALLOWED_ORIGINS = [
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000",
|
||||
]
|
||||
|
||||
# channel settings
|
||||
CHANNEL_LAYERS = {
|
||||
'default': {
|
||||
'BACKEND': 'channels.layers.InMemoryChannelLayer',
|
||||
},
|
||||
}
|
||||
|
||||
# # Office 365 settings
|
||||
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
# EMAIL_HOST = os.getenv("APP_EMAIL_HOST", "smtp.office365.com")
|
||||
# EMAIL_PORT = os.getenv("APP_EMAIL_PORT", 587)
|
||||
# EMAIL_HOST_USER = "ryan@aimloperations.com"#os.getenv("APP_EMAIL_HOST_USER")
|
||||
# SERVER_EMAIL = EMAIL_HOST_USER
|
||||
# DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
|
||||
# EMAIL_HOST_PASSWORD = "!HopeThisW0rkz"#os.getenv("APP_EMAIL_HOST_PASSWORD")
|
||||
# EMAIL_USE_TLS = os.getenv("APP_EMAIL_USE_TLS", True)
|
||||
# EMAIL_TIMEOUT = os.getenv("APP_EMAIL_TIMEOUT", 60)
|
||||
|
||||
# SMTP2GO
|
||||
EMAIL_HOST = 'mail.smtp2go.com'
|
||||
EMAIL_HOST_USER = 'info.aimloperations.com'
|
||||
EMAIL_HOST_PASSWORD = 'ZDErIII2sipNNVMz'
|
||||
EMAIL_PORT = 2525
|
||||
EMAIL_USE_TLS = True
|
||||
24
llm_be/llm_be/urls.py
Normal file
24
llm_be/llm_be/urls.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""llm_be URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.2/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
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', include('chat_backend.urls')),
|
||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
16
llm_be/llm_be/wsgi.py
Normal file
16
llm_be/llm_be/wsgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for llm_be 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/3.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'llm_be.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
22
llm_be/manage.py
Executable file
22
llm_be/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', 'llm_be.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()
|
||||
1
llm_be/scp_command.txt
Normal file
1
llm_be/scp_command.txt
Normal file
@@ -0,0 +1 @@
|
||||
scp -r ./* westfarn@10.0.0.103:/home/westfarn/chat_temp/
|
||||
133
requirements.dev
Normal file
133
requirements.dev
Normal file
@@ -0,0 +1,133 @@
|
||||
aiohappyeyeballs==2.4.4
|
||||
aiohttp==3.11.11
|
||||
aiosignal==1.3.2
|
||||
annotated-types==0.7.0
|
||||
anyio==4.7.0
|
||||
asgiref==3.8.1
|
||||
astor==0.8.1
|
||||
async-timeout==4.0.3
|
||||
attrs==24.3.0
|
||||
autobahn==24.4.2
|
||||
Automat==24.8.1
|
||||
black==24.10.0
|
||||
certifi==2024.12.14
|
||||
cffi==1.17.1
|
||||
channels==4.2.0
|
||||
charset-normalizer==3.4.1
|
||||
click==8.1.8
|
||||
constantly==23.10.4
|
||||
contourpy==1.3.0
|
||||
cryptography==44.0.0
|
||||
cycler==0.12.1
|
||||
daphne==4.1.2
|
||||
dataclasses-json==0.6.7
|
||||
distro==1.9.0
|
||||
Django==4.2.17
|
||||
django-autoslug==1.9.9
|
||||
django-cors-headers==4.6.0
|
||||
djangorestframework==3.15.2
|
||||
djangorestframework-simplejwt==5.3.1
|
||||
duckdb==1.1.3
|
||||
et_xmlfile==2.0.0
|
||||
exceptiongroup==1.2.2
|
||||
Faker==33.1.0
|
||||
filelock==3.16.1
|
||||
fonttools==4.55.3
|
||||
frozenlist==1.5.0
|
||||
fsspec==2024.12.0
|
||||
greenlet==3.1.1
|
||||
h11==0.14.0
|
||||
httpcore==1.0.7
|
||||
httpx==0.27.2
|
||||
httpx-sse==0.4.0
|
||||
hyperlink==21.0.0
|
||||
idna==3.10
|
||||
importlib_resources==6.4.5
|
||||
incremental==24.7.2
|
||||
Jinja2==3.1.5
|
||||
jiter==0.8.2
|
||||
jsonpatch==1.33
|
||||
jsonpointer==3.0.0
|
||||
kiwisolver==1.4.7
|
||||
langchain==0.3.13
|
||||
langchain-community==0.3.13
|
||||
langchain-core==0.3.28
|
||||
langchain-ollama==0.2.2
|
||||
langchain-openai==0.2.14
|
||||
langchain-text-splitters==0.3.4
|
||||
langsmith==0.2.7
|
||||
lxml==5.3.0
|
||||
MarkupSafe==3.0.2
|
||||
marshmallow==3.23.2
|
||||
matplotlib==3.9.4
|
||||
mpmath==1.3.0
|
||||
multidict==6.1.0
|
||||
mypy-extensions==1.0.0
|
||||
networkx==3.2.1
|
||||
numpy==2.0.2
|
||||
nvidia-cublas-cu12==12.4.5.8
|
||||
nvidia-cuda-cupti-cu12==12.4.127
|
||||
nvidia-cuda-nvrtc-cu12==12.4.127
|
||||
nvidia-cuda-runtime-cu12==12.4.127
|
||||
nvidia-cudnn-cu12==9.1.0.70
|
||||
nvidia-cufft-cu12==11.2.1.3
|
||||
nvidia-curand-cu12==10.3.5.147
|
||||
nvidia-cusolver-cu12==11.6.1.9
|
||||
nvidia-cusparse-cu12==12.3.1.170
|
||||
nvidia-nccl-cu12==2.21.5
|
||||
nvidia-nvjitlink-cu12==12.4.127
|
||||
nvidia-nvtx-cu12==12.4.127
|
||||
ollama==0.4.5
|
||||
ollama-python==0.1.2
|
||||
openai==1.58.1
|
||||
openpyxl==3.1.5
|
||||
orjson==3.10.13
|
||||
packaging==24.2
|
||||
pandas==2.2.3
|
||||
pandasai==2.4.1
|
||||
pathspec==0.12.1
|
||||
pillow==11.0.0
|
||||
platformdirs==4.3.6
|
||||
propcache==0.2.1
|
||||
pyasn1==0.6.1
|
||||
pyasn1_modules==0.4.1
|
||||
pycparser==2.22
|
||||
pydantic==2.10.4
|
||||
pydantic-settings==2.7.1
|
||||
pydantic_core==2.27.2
|
||||
PyJWT==2.10.1
|
||||
pyOpenSSL==24.3.0
|
||||
pyparsing==3.2.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-docx==1.1.2
|
||||
python-dotenv==1.0.1
|
||||
pytz==2024.2
|
||||
PyYAML==6.0.2
|
||||
regex==2024.11.6
|
||||
requests==2.32.3
|
||||
requests-toolbelt==1.0.0
|
||||
responses==0.25.3
|
||||
scipy==1.13.1
|
||||
service-identity==24.2.0
|
||||
six==1.17.0
|
||||
sniffio==1.3.1
|
||||
SQLAlchemy==2.0.36
|
||||
sqlglot==26.0.1
|
||||
sqlglotrs==0.3.0
|
||||
sqlparse==0.5.3
|
||||
sympy==1.13.1
|
||||
tenacity==9.0.0
|
||||
tiktoken==0.8.0
|
||||
tomli==2.2.1
|
||||
torch==2.5.1
|
||||
tqdm==4.67.1
|
||||
triton==3.1.0
|
||||
Twisted==24.11.0
|
||||
txaio==23.1.1
|
||||
typing-inspect==0.9.0
|
||||
typing_extensions==4.12.2
|
||||
tzdata==2024.2
|
||||
urllib3==2.3.0
|
||||
yarl==1.18.3
|
||||
zipp==3.21.0
|
||||
zope.interface==7.2
|
||||
1
upgrade_all_package.sh
Executable file
1
upgrade_all_package.sh
Executable file
@@ -0,0 +1 @@
|
||||
python -m pip --disable-pip-version-check list --outdated --format=json | python -c "import json, sys; print('\n'.join([x['name'] for x in json.load(sys.stdin)]))" | xargs -n1 pip install -U
|
||||
Reference in New Issue
Block a user