2023-06-28 20:39:27 +03:00
|
|
|
from typing import Any, List, Optional
|
2023-06-17 00:36:53 +03:00
|
|
|
from uuid import UUID
|
|
|
|
|
2023-07-11 13:09:56 +03:00
|
|
|
from logger import get_logger
|
2023-07-25 16:22:17 +03:00
|
|
|
from pydantic import BaseModel
|
|
|
|
from utils.vectors import get_unique_files_from_vector_ids
|
2023-07-18 15:30:19 +03:00
|
|
|
|
2023-08-01 17:25:02 +03:00
|
|
|
from models.settings import BrainRateLimiting, CommonsDep, common_dependencies
|
|
|
|
from models.users import User
|
|
|
|
|
2023-07-11 13:09:56 +03:00
|
|
|
logger = get_logger(__name__)
|
2023-07-10 15:27:49 +03:00
|
|
|
|
2023-06-17 00:36:53 +03:00
|
|
|
|
|
|
|
class Brain(BaseModel):
|
2023-06-28 20:39:27 +03:00
|
|
|
id: Optional[UUID] = None
|
|
|
|
name: Optional[str] = "Default brain"
|
2023-07-25 16:22:17 +03:00
|
|
|
description: Optional[str] = "This is a description"
|
|
|
|
status: Optional[str] = "private"
|
2023-06-17 00:36:53 +03:00
|
|
|
model: Optional[str] = "gpt-3.5-turbo-0613"
|
|
|
|
temperature: Optional[float] = 0.0
|
|
|
|
max_tokens: Optional[int] = 256
|
2023-07-25 16:22:17 +03:00
|
|
|
openai_api_key: Optional[str] = None
|
2023-06-28 20:39:27 +03:00
|
|
|
files: List[Any] = []
|
2023-07-24 15:17:33 +03:00
|
|
|
max_brain_size = BrainRateLimiting().max_brain_size
|
2023-06-21 11:16:44 +03:00
|
|
|
|
|
|
|
class Config:
|
|
|
|
arbitrary_types_allowed = True
|
|
|
|
|
|
|
|
@property
|
|
|
|
def commons(self) -> CommonsDep:
|
2023-07-11 19:20:31 +03:00
|
|
|
return common_dependencies()
|
2023-06-21 11:16:44 +03:00
|
|
|
|
2023-06-28 20:39:27 +03:00
|
|
|
@property
|
|
|
|
def brain_size(self):
|
|
|
|
self.get_unique_brain_files()
|
2023-07-02 03:19:30 +03:00
|
|
|
current_brain_size = sum(float(doc["size"]) for doc in self.files)
|
2023-06-28 20:39:27 +03:00
|
|
|
|
|
|
|
return current_brain_size
|
|
|
|
|
|
|
|
@property
|
|
|
|
def remaining_brain_size(self):
|
2023-07-10 15:27:49 +03:00
|
|
|
return (
|
|
|
|
float(self.max_brain_size) # pyright: ignore reportPrivateUsage=none
|
|
|
|
- self.brain_size # pyright: ignore reportPrivateUsage=none
|
|
|
|
)
|
2023-06-28 20:39:27 +03:00
|
|
|
|
2023-06-21 11:16:44 +03:00
|
|
|
@classmethod
|
|
|
|
def create(cls, *args, **kwargs):
|
|
|
|
commons = common_dependencies()
|
2023-07-10 15:27:49 +03:00
|
|
|
return cls(
|
|
|
|
commons=commons, *args, **kwargs # pyright: ignore reportPrivateUsage=none
|
|
|
|
) # pyright: ignore reportPrivateUsage=none
|
2023-06-21 11:16:44 +03:00
|
|
|
|
2023-07-13 19:05:36 +03:00
|
|
|
# TODO: move this to a brand new BrainService
|
|
|
|
def get_brain_users(self):
|
|
|
|
response = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("brains_users")
|
|
|
|
.select("id:brain_id, *")
|
|
|
|
.filter("brain_id", "eq", self.id)
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
return response.data
|
|
|
|
|
|
|
|
# TODO: move this to a brand new BrainService
|
|
|
|
def delete_user_from_brain(self, user_id):
|
|
|
|
results = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("brains_users")
|
|
|
|
.select("*")
|
|
|
|
.match({"brain_id": self.id, "user_id": user_id})
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
|
|
|
|
if len(results.data) != 0:
|
|
|
|
self.commons["supabase"].table("brains_users").delete().match(
|
|
|
|
{"brain_id": self.id, "user_id": user_id}
|
|
|
|
).execute()
|
|
|
|
|
2023-06-21 11:16:44 +03:00
|
|
|
def get_user_brains(self, user_id):
|
|
|
|
response = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.from_("brains_users")
|
2023-07-18 15:30:19 +03:00
|
|
|
.select("id:brain_id, rights, brains (id: brain_id, name)")
|
2023-06-21 11:16:44 +03:00
|
|
|
.filter("user_id", "eq", user_id)
|
|
|
|
.execute()
|
|
|
|
)
|
2023-07-18 15:30:19 +03:00
|
|
|
user_brains = []
|
|
|
|
for item in response.data:
|
|
|
|
user_brains.append(item["brains"])
|
|
|
|
user_brains[-1]["rights"] = item["rights"]
|
|
|
|
return user_brains
|
2023-06-21 11:16:44 +03:00
|
|
|
|
2023-07-11 11:00:06 +03:00
|
|
|
def get_brain_for_user(self, user_id):
|
|
|
|
response = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.from_("brains_users")
|
|
|
|
.select("id:brain_id, rights, brains (id: brain_id, name)")
|
|
|
|
.filter("user_id", "eq", user_id)
|
|
|
|
.filter("brain_id", "eq", self.id)
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
if len(response.data) == 0:
|
|
|
|
return None
|
|
|
|
return response.data[0]
|
|
|
|
|
2023-06-21 11:16:44 +03:00
|
|
|
def get_brain_details(self):
|
|
|
|
response = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.from_("brains")
|
|
|
|
.select("id:brain_id, name, *")
|
2023-06-28 20:39:27 +03:00
|
|
|
.filter("brain_id", "eq", self.id)
|
2023-06-21 11:16:44 +03:00
|
|
|
.execute()
|
|
|
|
)
|
2023-08-01 17:25:02 +03:00
|
|
|
if response.data == []:
|
|
|
|
return None
|
|
|
|
return response.data[0]
|
2023-06-21 11:16:44 +03:00
|
|
|
|
2023-07-02 03:19:30 +03:00
|
|
|
def delete_brain(self, user_id):
|
|
|
|
results = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("brains_users")
|
|
|
|
.select("*")
|
|
|
|
.match({"brain_id": self.id, "user_id": user_id, "rights": "Owner"})
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
if len(results.data) == 0:
|
|
|
|
return {"message": "You are not the owner of this brain."}
|
|
|
|
else:
|
|
|
|
results = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("brains_vectors")
|
|
|
|
.delete()
|
|
|
|
.match({"brain_id": self.id})
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
|
|
|
|
results = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("brains_users")
|
|
|
|
.delete()
|
|
|
|
.match({"brain_id": self.id})
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
|
|
|
|
results = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("brains")
|
|
|
|
.delete()
|
|
|
|
.match({"brain_id": self.id})
|
|
|
|
.execute()
|
|
|
|
)
|
2023-06-21 11:16:44 +03:00
|
|
|
|
2023-06-28 20:39:27 +03:00
|
|
|
def create_brain(self):
|
2023-06-21 11:16:44 +03:00
|
|
|
commons = common_dependencies()
|
2023-07-02 03:19:30 +03:00
|
|
|
response = (
|
2023-07-25 16:22:17 +03:00
|
|
|
commons["supabase"]
|
|
|
|
.table("brains")
|
|
|
|
.insert(
|
|
|
|
{
|
|
|
|
"name": self.name,
|
|
|
|
"description": self.description,
|
|
|
|
"temperature": self.temperature,
|
|
|
|
"model": self.model,
|
|
|
|
"max_tokens": self.max_tokens,
|
|
|
|
"openai_api_key": self.openai_api_key,
|
|
|
|
"status": self.status,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
.execute()
|
2023-07-02 03:19:30 +03:00
|
|
|
)
|
2023-06-28 20:39:27 +03:00
|
|
|
|
2023-07-02 03:19:30 +03:00
|
|
|
self.id = response.data[0]["brain_id"]
|
2023-06-21 11:16:44 +03:00
|
|
|
return response.data
|
|
|
|
|
2023-07-14 22:02:26 +03:00
|
|
|
def create_brain_user(self, user_id: UUID, rights, default_brain: bool):
|
2023-06-28 20:39:27 +03:00
|
|
|
commons = common_dependencies()
|
2023-07-02 03:19:30 +03:00
|
|
|
response = (
|
|
|
|
commons["supabase"]
|
|
|
|
.table("brains_users")
|
|
|
|
.insert(
|
|
|
|
{
|
|
|
|
"brain_id": str(self.id),
|
|
|
|
"user_id": str(user_id),
|
|
|
|
"rights": rights,
|
|
|
|
"default_brain": default_brain,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
|
2023-06-21 11:16:44 +03:00
|
|
|
return response.data
|
|
|
|
|
2023-07-25 16:22:17 +03:00
|
|
|
def set_as_default_brain_for_user(self, user: User):
|
|
|
|
old_default_brain = get_default_user_brain(user)
|
|
|
|
|
|
|
|
if old_default_brain is not None:
|
|
|
|
self.commons["supabase"].table("brains_users").update(
|
|
|
|
{"default_brain": False}
|
|
|
|
).match({"brain_id": old_default_brain["id"], "user_id": user.id}).execute()
|
|
|
|
|
|
|
|
self.commons["supabase"].table("brains_users").update(
|
|
|
|
{"default_brain": True}
|
|
|
|
).match({"brain_id": self.id, "user_id": user.id}).execute()
|
|
|
|
|
2023-06-29 19:26:03 +03:00
|
|
|
def create_brain_vector(self, vector_id, file_sha1):
|
2023-06-21 11:16:44 +03:00
|
|
|
response = (
|
|
|
|
self.commons["supabase"]
|
2023-06-28 20:39:27 +03:00
|
|
|
.table("brains_vectors")
|
2023-07-02 03:19:30 +03:00
|
|
|
.insert(
|
|
|
|
{
|
|
|
|
"brain_id": str(self.id),
|
|
|
|
"vector_id": str(vector_id),
|
|
|
|
"file_sha1": file_sha1,
|
|
|
|
}
|
|
|
|
)
|
2023-06-21 11:16:44 +03:00
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
return response.data
|
|
|
|
|
|
|
|
def get_vector_ids_from_file_sha1(self, file_sha1: str):
|
2023-06-28 20:39:27 +03:00
|
|
|
# move to vectors class
|
2023-06-21 11:16:44 +03:00
|
|
|
vectorsResponse = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("vectors")
|
|
|
|
.select("id")
|
|
|
|
.filter("metadata->>file_sha1", "eq", file_sha1)
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
return vectorsResponse.data
|
|
|
|
|
|
|
|
def update_brain_fields(self):
|
2023-07-25 16:22:17 +03:00
|
|
|
self.commons["supabase"].table("brains").update(
|
|
|
|
{
|
|
|
|
"name": self.name,
|
|
|
|
"description": self.description,
|
|
|
|
"model": self.model,
|
|
|
|
"temperature": self.temperature,
|
|
|
|
"max_tokens": self.max_tokens,
|
|
|
|
"openai_api_key": self.openai_api_key,
|
|
|
|
"status": self.status,
|
|
|
|
}
|
|
|
|
).match({"brain_id": self.id}).execute()
|
2023-06-28 20:39:27 +03:00
|
|
|
|
|
|
|
def get_unique_brain_files(self):
|
|
|
|
"""
|
|
|
|
Retrieve unique brain data (i.e. uploaded files and crawled websites).
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = (
|
2023-07-02 03:19:30 +03:00
|
|
|
self.commons["supabase"]
|
|
|
|
.from_("brains_vectors")
|
|
|
|
.select("vector_id")
|
|
|
|
.filter("brain_id", "eq", self.id)
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
|
2023-06-28 20:39:27 +03:00
|
|
|
vector_ids = [item["vector_id"] for item in response.data]
|
|
|
|
|
|
|
|
if len(vector_ids) == 0:
|
|
|
|
return []
|
2023-07-02 03:19:30 +03:00
|
|
|
|
2023-07-05 15:37:01 +03:00
|
|
|
self.files = get_unique_files_from_vector_ids(vector_ids)
|
2023-06-28 20:39:27 +03:00
|
|
|
|
|
|
|
return self.files
|
|
|
|
|
|
|
|
def delete_file_from_brain(self, file_name: str):
|
|
|
|
# First, get the vector_ids associated with the file_name
|
2023-07-02 03:19:30 +03:00
|
|
|
vector_response = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("vectors")
|
|
|
|
.select("id")
|
|
|
|
.filter("metadata->>file_name", "eq", file_name)
|
|
|
|
.execute()
|
|
|
|
)
|
2023-06-28 20:39:27 +03:00
|
|
|
vector_ids = [item["id"] for item in vector_response.data]
|
|
|
|
|
|
|
|
# For each vector_id, delete the corresponding entry from the 'brains_vectors' table
|
|
|
|
for vector_id in vector_ids:
|
2023-07-02 03:19:30 +03:00
|
|
|
self.commons["supabase"].table("brains_vectors").delete().filter(
|
|
|
|
"vector_id", "eq", vector_id
|
|
|
|
).filter("brain_id", "eq", self.id).execute()
|
2023-06-28 20:39:27 +03:00
|
|
|
|
|
|
|
# Check if the vector is still associated with any other brains
|
2023-07-02 03:19:30 +03:00
|
|
|
associated_brains_response = (
|
|
|
|
self.commons["supabase"]
|
|
|
|
.table("brains_vectors")
|
|
|
|
.select("brain_id")
|
|
|
|
.filter("vector_id", "eq", vector_id)
|
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
associated_brains = [
|
|
|
|
item["brain_id"] for item in associated_brains_response.data
|
|
|
|
]
|
2023-06-28 20:39:27 +03:00
|
|
|
|
|
|
|
# If the vector is not associated with any other brains, delete it from 'vectors' table
|
|
|
|
if not associated_brains:
|
2023-07-02 03:19:30 +03:00
|
|
|
self.commons["supabase"].table("vectors").delete().filter(
|
|
|
|
"id", "eq", vector_id
|
|
|
|
).execute()
|
2023-06-28 20:39:27 +03:00
|
|
|
|
|
|
|
return {"message": f"File {file_name} in brain {self.id} has been deleted."}
|
|
|
|
|
2023-07-02 03:19:30 +03:00
|
|
|
|
2023-06-28 20:39:27 +03:00
|
|
|
def get_default_user_brain(user: User):
|
|
|
|
commons = common_dependencies()
|
|
|
|
response = (
|
|
|
|
commons["supabase"]
|
2023-07-11 13:09:56 +03:00
|
|
|
.from_("brains_users")
|
2023-07-02 03:19:30 +03:00
|
|
|
.select("brain_id")
|
2023-06-28 20:39:27 +03:00
|
|
|
.filter("user_id", "eq", user.id)
|
2023-07-11 13:09:56 +03:00
|
|
|
.filter("default_brain", "eq", True)
|
2023-06-28 20:39:27 +03:00
|
|
|
.execute()
|
|
|
|
)
|
|
|
|
|
|
|
|
default_brain_id = response.data[0]["brain_id"] if response.data else None
|
|
|
|
|
2023-07-11 13:09:56 +03:00
|
|
|
logger.info(f"Default brain id: {default_brain_id}")
|
2023-06-28 20:39:27 +03:00
|
|
|
|
|
|
|
if default_brain_id:
|
|
|
|
brain_response = (
|
|
|
|
commons["supabase"]
|
|
|
|
.from_("brains")
|
|
|
|
.select("id:brain_id, name, *")
|
|
|
|
.filter("brain_id", "eq", default_brain_id)
|
|
|
|
.execute()
|
|
|
|
)
|
2023-07-02 03:19:30 +03:00
|
|
|
|
2023-06-28 20:39:27 +03:00
|
|
|
return brain_response.data[0] if brain_response.data else None
|
|
|
|
|
2023-07-14 22:02:26 +03:00
|
|
|
|
|
|
|
def get_default_user_brain_or_create_new(user: User) -> Brain:
|
|
|
|
default_brain = get_default_user_brain(user)
|
|
|
|
|
|
|
|
if default_brain:
|
|
|
|
return Brain.create(**default_brain)
|
|
|
|
else:
|
|
|
|
brain = Brain.create()
|
|
|
|
brain.create_brain()
|
|
|
|
brain.create_brain_user(user.id, "Owner", True)
|
|
|
|
return brain
|