temp checkin

This commit is contained in:
2025-03-07 12:23:00 -06:00
parent 058f961b0d
commit 57695353d0
53 changed files with 2786 additions and 2 deletions

View File

@@ -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
View 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

View 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>

View 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
View 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="&lt;map/&gt;" />
<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
View 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
View 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
View 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>

View File

View 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)

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ChatBackendConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "chat_backend"

View 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)

View 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()),
],
),
]

View 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)),
],
),
]

View File

@@ -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",
),
),
]

View File

@@ -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",
),
]

View File

@@ -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",
),
),
]

View File

@@ -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",
),
),
]

View File

@@ -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",
),
),
]

View File

@@ -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,
),
),
]

View File

@@ -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,
),
),
]

View File

@@ -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"
),
),
]

View File

@@ -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"
),
),
]

View 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"),
),
]

View 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"),
),
]

View 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,
),
),
],
),
]

View File

@@ -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),
),
]

View File

@@ -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,
),
),
]

View File

@@ -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
),
),
]

View File

@@ -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,
),
),
]

View File

@@ -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"
),
),
]

View 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

View 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

View 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()),
]

View 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')

View 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>&copy; 2025 AI ML Operations, LLC. All rights reserved.</p>
</div>
</div>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -0,0 +1,3 @@
New feedback for Chat by AI ML Operations, LLC
"New Feedback. {{ title }}. {{ feedback_text }}"

View 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>&copy; 2025 AI ML Operations, LLC. All rights reserved.</p>
</div>
</div>
</td>
</tr>
</table>
</body>
</html>

View 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"

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View 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"),
]

View 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)

View 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")

View File

25
llm_be/llm_be/asgi.py Normal file
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
scp -r ./* westfarn@10.0.0.103:/home/westfarn/chat_temp/

133
requirements.dev Normal file
View 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
View 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