Merge branch 'temporary_branch' of https://github.com/QuickfoxConsulting/privateGPT into temporary_branch

This commit is contained in:
Saurab-Shrestha 2024-04-09 15:48:44 +05:45
commit aab5e50f7c
29 changed files with 566 additions and 163 deletions

2
.env
View file

@ -5,7 +5,7 @@ DB_HOST=localhost
DB_USER=postgres
DB_PORT=5432
DB_PASSWORD=admin
DB_NAME=openai
DB_NAME=QuickGpt
SUPER_ADMIN_EMAIL=superadmin@email.com
SUPER_ADMIN_PASSWORD=supersecretpassword

View file

@ -33,7 +33,7 @@ COPY --chown=worker private_gpt/ private_gpt
COPY --chown=worker fern/ fern
COPY --chown=worker *.yaml *.md ./
COPY --chown=worker scripts/ scripts
RUN poetry run python scripts/setup
RUN
ENV PYTHONPATH="$PYTHONPATH:/private_gpt/"

View file

@ -0,0 +1,53 @@
"""Changes
Revision ID: 57167fd38273
Revises:
Create Date: 2024-04-07 12:33:40.975147
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '57167fd38273'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('chat_history',
sa.Column('conversation_id', sa.UUID(), nullable=False),
sa.Column('title', sa.String(length=255), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('_title_generated', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('conversation_id')
)
op.create_table('chat_items',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('sender', sa.String(length=225), nullable=False),
sa.Column('content', sa.JSON(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('like', sa.Boolean(), nullable=True),
sa.Column('conversation_id', sa.UUID(), nullable=False),
sa.ForeignKeyConstraint(['conversation_id'], ['chat_history.conversation_id'], ),
sa.PrimaryKeyConstraint('id')
)
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
op.drop_table('chat_items')
op.drop_table('chat_history')
# ### end Alembic commands ###

View file

@ -0,0 +1,39 @@
"""Changes content to json
Revision ID: 5f1c1d3934a1
Revises: eb18396f592a
Create Date: 2024-04-07 12:29:18.009919
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '5f1c1d3934a1'
down_revision: Union[str, None] = 'eb18396f592a'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('chat_items', 'content',
existing_type=sa.TEXT(),
type_=sa.JSON(),
existing_nullable=True,
)
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
op.alter_column('chat_items', 'content',
existing_type=sa.JSON(),
type_=sa.TEXT(),
existing_nullable=True)
# ### end Alembic commands ###

View file

@ -0,0 +1,52 @@
"""create chat history and items
Revision ID: 9957402017dc
Revises:
Create Date: 2024-04-04 11:31:53.261330
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '9957402017dc'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('chat_history',
sa.Column('conversation_id', sa.UUID(), nullable=False),
sa.Column('title', sa.String(length=255), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('conversation_id')
)
op.create_table('chat_items',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('sender', sa.String(length=225), nullable=False),
sa.Column('content', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('like', sa.Boolean(), nullable=True),
sa.Column('conversation_id', sa.UUID(), nullable=False),
sa.ForeignKeyConstraint(['conversation_id'], ['chat_history.conversation_id'], ),
sa.PrimaryKeyConstraint('id')
)
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
op.drop_table('chat_items')
op.drop_table('chat_history')
# ### end Alembic commands ###

View file

@ -1,38 +0,0 @@
"""update
Revision ID: b7b896502e8e
Revises:
Create Date: 2024-03-17 15:07:10.795935
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'b7b896502e8e'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('document_department_association_department_id_fkey', 'document_department_association', type_='foreignkey')
op.drop_constraint('document_department_association_document_id_fkey', 'document_department_association', type_='foreignkey')
op.create_foreign_key(None, 'document_department_association', 'document', ['document_id'], ['id'], onupdate='CASCADE', ondelete='CASCADE')
op.create_foreign_key(None, 'document_department_association', 'departments', ['department_id'], ['id'], onupdate='CASCADE', ondelete='CASCADE')
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
op.drop_constraint(None, 'document_department_association', type_='foreignkey')
op.drop_constraint(None, 'document_department_association', type_='foreignkey')
op.create_foreign_key('document_department_association_document_id_fkey', 'document_department_association', 'document', ['document_id'], ['id'])
op.create_foreign_key('document_department_association_department_id_fkey', 'document_department_association', 'departments', ['department_id'], ['id'])
# ### end Alembic commands ###

View file

@ -1,8 +1,8 @@
"""Update is_enabled to false by default
"""create title event
Revision ID: 14281ff34686
Revises: b7b896502e8e
Create Date: 2024-03-18 16:33:43.133458
Revision ID: eb18396f592a
Revises: 9957402017dc
Create Date: 2024-04-04 11:47:55.600187
"""
from typing import Sequence, Union
@ -12,21 +12,21 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '14281ff34686'
down_revision: Union[str, None] = 'b7b896502e8e'
revision: str = 'eb18396f592a'
down_revision: Union[str, None] = '9957402017dc'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('chat_history', sa.Column('_title_generated', sa.Boolean(), nullable=True))
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
# ### end Alembic commands ###
pass
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
pass
op.drop_column('chat_history', '_title_generated')
# ### end Alembic commands ###

View file

@ -4,4 +4,8 @@
`alembic revision --autogenerate -m "Create user model"` # first migration
`alembic upgrade 66b63a` # reflect migration on database (here 66b63a) is ssh value
`alembic upgrade 66b63a` # reflect migration on database (here 66b63a) is ssh value
## Local installation
`poetry install --extras "ui llms-llama-cpp embeddings-huggingface vector-stores-qdrant rerank-sentence-transformers"`

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
poetry.lock generated
View file

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "aiofiles"
@ -6511,6 +6511,16 @@ h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "uuid"
version = "1.30"
description = "UUID object and generation functions (Python 2.3 or higher)"
optional = false
python-versions = "*"
files = [
{file = "uuid-1.30.tar.gz", hash = "sha256:1f87cc004ac5120466f36c5beae48b4c48cc411968eed0eaecd3da82aa96193f"},
]
[[package]]
name = "uvicorn"
version = "0.28.0"
@ -7147,4 +7157,4 @@ vector-stores-qdrant = ["llama-index-vector-stores-qdrant"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.11,<3.12"
content-hash = "b0715db49b2b01f8b6c1b3d122d93de58ef052be999114390fab9fa19cc9e794"
content-hash = "57d64e26ba5f7100c0f6d61135808b190a9f6b8c95a6407b984b7cbea244d5f5"

View file

@ -3,7 +3,7 @@ import logging
from injector import inject, singleton
from llama_index.core.embeddings import BaseEmbedding, MockEmbedding
from private_gpt.paths import models_cache_path
from private_gpt.paths import models_cache_path, models_path
from private_gpt.settings.settings import Settings
logger = logging.getLogger(__name__)
@ -27,7 +27,6 @@ class EmbeddingComponent:
raise ImportError(
"Local dependencies not found, install with `poetry install --extras embeddings-huggingface`"
) from e
self.embedding_model = HuggingFaceEmbedding(
model_name=settings.huggingface.embedding_hf_model_name,
cache_folder=str(models_cache_path),

View file

@ -11,8 +11,7 @@ from private_gpt.paths import models_cache_path, models_path
from private_gpt.settings.settings import Settings
logger = logging.getLogger(__name__)
local_path = models_path / "tokenizer/Mistral-7B-Instruct-v0.1"
@singleton
class LLMComponent:
llm: LLM
@ -23,8 +22,7 @@ class LLMComponent:
if settings.llm.tokenizer:
set_global_tokenizer(
AutoTokenizer.from_pretrained(
pretrained_model_name_or_path=settings.llm.tokenizer,
cache_dir=str(models_cache_path),
local_path
)
)

View file

@ -54,6 +54,7 @@ def create_app(root_injector: Injector) -> FastAPI:
"http://192.168.1.98", "http://192.168.1.98:5173", "http://localhost:3000","https://globaldocquery.gibl.com.np/", "http://127.0.0.1/", "http://localhost/",
"http://localhost:80", "http://192.168.1.131", 'http://192.168.1.131:3000'
, "http://192.168.1.127", 'http://192.168.1.127:3000'
, "http://192.168.1.70", 'http://192.168.1.70:3000'
],
allow_methods=["DELETE", "GET", "POST", "PUT", "OPTIONS", "PATCH"],
allow_headers=["*"],

View file

@ -4,10 +4,11 @@ from llama_index.llms import ChatMessage, ChatResponse, MessageRole
from fastapi import APIRouter, Depends, Request, Security, HTTPException, status
from private_gpt.server.ingest.ingest_service import IngestService
from pydantic import BaseModel
from typing import List, Dict, Any
from sqlalchemy.orm import Session
import traceback
import logging
import json
logger = logging.getLogger(__name__)
from starlette.responses import StreamingResponse
@ -20,13 +21,13 @@ from private_gpt.open_ai.openai_models import (
from private_gpt.server.chat.chat_router import ChatBody, chat_completion
from private_gpt.server.utils.auth import authenticated
from private_gpt.users.api import deps
from pydantic import Optional
from private_gpt.users import crud, models, schemas
import uuid
completions_router = APIRouter(prefix="/v1", dependencies=[Depends(authenticated)])
class CompletionsBody(BaseModel):
conversation_id: Optional[int]
conversation_id: uuid.UUID
prompt: str
system_prompt: str | None = None
use_context: bool = False
@ -38,6 +39,7 @@ class CompletionsBody(BaseModel):
"json_schema_extra": {
"examples": [
{
"conversation_id": 123,
"prompt": "How do you fry an egg?",
"system_prompt": "You are a rapper. Always answer with a rap.",
"stream": False,
@ -49,6 +51,9 @@ class CompletionsBody(BaseModel):
}
class ChatContentCreate(BaseModel):
content: Dict[str, Any]
# @completions_router.post(
# "/completions",
# response_model=None,
@ -97,6 +102,14 @@ class CompletionsBody(BaseModel):
# )
# return chat_completion(request, chat_body)
def create_chat_item(db, sender, content, conversation_id):
chat_item_create = schemas.ChatItemCreate(
sender=sender,
content=content,
conversation_id=conversation_id
)
return crud.chat_item.create(db, obj_in=chat_item_create)
@completions_router.post(
"/chat",
@ -135,97 +148,59 @@ async def prompt_completion(
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail=f"No documents uploaded for your department.")
docs_list = [document.filename for document in documents]
print("DOCUMENTS ASSIGNED TO THIS DEPARTMENTS: ", docs_list)
docs_ids = []
for filename in docs_list:
doc_id = service.get_doc_ids_by_filename(filename)
docs_ids.extend(doc_id)
body.context_filter = {"docs_ids": docs_ids}
chat_history = crud.chat.get_by_id(
db, id=body.conversation_id
)
if (chat_history is None) and (chat_history.user_id != current_user.id):
raise HTTPException(
status_code=404, detail="Chat history not found")
user_message = OpenAIMessage(content=body.prompt, role="user")
user_message = user_message.model_dump(mode="json")
user_message_json = {
'text': body.prompt,
}
create_chat_item(db, "user", user_message_json , body.conversation_id)
messages = [user_message]
if body.system_prompt:
messages.insert(0, OpenAIMessage(
content=body.system_prompt, role="system"))
chat_body = ChatBody(
messages=messages,
use_context=body.use_context,
stream=body.stream,
include_sources=body.include_sources,
context_filter=body.context_filter,
)
log_audit(
model='Chat',
action='Chat',
details={
"query": body.prompt,
'user': current_user.username,
},
user_id=current_user.id
)
chat_response = await chat_completion(request, chat_body)
ai_response = chat_response.model_dump(mode="json")
create_chat_item(db, "assistant", ai_response, body.conversation_id)
return chat_response
except Exception as e:
print(traceback.format_exc())
logger.error(f"There was an error: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Internal Server Error",
)
if body.conversation_id:
chat_history = crud.chat.get_by_id(db, id=body.conversation_id)
if chat_history is None or chat_history.user_id != current_user.id:
raise HTTPException(
status_code=404, detail="Chat history not found")
else:
chat_create_in = schemas.ChatCreate(user_id=current_user.id)
chat_history = crud.chat.create(db=db, obj_in=chat_create_in)
_history = chat_history.messages or []
def build_history() -> list[ChatMessage]:
history_messages: list[ChatMessage] = []
for interaction in _history:
user_message = interaction.get("user", "")
ai_message = interaction.get("ai", "")
if user_message:
history_messages.append(
ChatMessage(
content=user_message,
role=MessageRole.USER
)
)
if ai_message:
history_messages.append(
ChatMessage(
content=ai_message,
role=MessageRole.ASSISTANT
)
)
# max 20 messages to try to avoid context overflow
return history_messages[:20]
# Prepare new messages
new_messages = []
if body.prompt:
new_messages.append(OpenAIMessage(content=body.prompt, role="user"))
if body.system_prompt:
new_messages.insert(0, OpenAIMessage(
content=body.system_prompt, role="system"))
# Update chat history with new user messages
if new_messages:
new_message = ChatMessage(content=new_messages, role=MessageRole.USER)
_history.append(new_message.dict())
# Process chat completion
chat_body = ChatBody(
messages=build_history(),
use_context=body.use_context,
stream=body.stream,
include_sources=body.include_sources,
context_filter=body.context_filter,
)
ai_response = await chat_completion(request, chat_body)
# Update chat history with AI response
if ai_response.messages:
ai_message = OpenAIMessage(
content=ai_response.messages, role="assistant")
_history.append(ai_message.dict())
# Update chat history in the database
chat_obj_in = schemas.ChatUpdate(messages=build_history())
crud.chat.update_messages(db, db_obj=chat_history, obj_in=chat_obj_in)
return ai_response
# log_audit(
# model='Chat',
# action='Chat',
# details={
# "query": body.prompt,
# 'user': current_user.username,
# },
# user_id=current_user.id
# )
)

View file

@ -1,5 +1,5 @@
from private_gpt.users.api import deps
from private_gpt.users.api.v1.routers import auth, roles, user_roles, users, subscriptions, companies, departments, documents, audits
from private_gpt.users.api.v1.routers import auth, roles, user_roles, users, subscriptions, companies, departments, documents, audits, chat_history
from fastapi import APIRouter
api_router = APIRouter(prefix="/v1")
@ -13,4 +13,5 @@ api_router.include_router(subscriptions.router)
api_router.include_router(departments.router)
api_router.include_router(documents.router)
api_router.include_router(audits.router)
api_router.include_router(chat_history.router)

View file

@ -0,0 +1,125 @@
import logging
import traceback
import uuid
from sqlalchemy.orm import Session
from fastapi.responses import JSONResponse
from fastapi import APIRouter, Depends, HTTPException, status, Security
from private_gpt.users.api import deps
from private_gpt.users import crud, models, schemas
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/c", tags=["Chat Histories"])
@router.get("", response_model=list[schemas.ChatHistory])
def list_chat_histories(
db: Session = Depends(deps.get_db),
skip: int = 0,
limit: int = 100,
current_user: models.User = Security(
deps.get_current_user,
),
) -> list[schemas.ChatHistory]:
"""
Retrieve a list of chat histories with pagination support.
"""
try:
chat_histories = crud.chat.get_chat_history(
db, skip=skip, limit=limit)
return chat_histories
except Exception as e:
print(traceback.format_exc())
logger.error(f"Error listing chat histories: {str(e)}")
raise HTTPException(
status_code=500,
detail="Internal Server Error",
)
@router.post("/create", response_model=schemas.ChatHistory)
def create_chat_history(
db: Session = Depends(deps.get_db),
current_user: models.User = Security(
deps.get_current_user,
),
) -> schemas.ChatHistory:
"""
Create a new chat history
"""
try:
chat_history_in = schemas.CreateChatHistory(
user_id= current_user.id
)
chat_history = crud.chat.create(
db=db, obj_in=chat_history_in)
return chat_history
except Exception as e:
print(traceback.format_exc())
logger.error(f"Error creating chat history: {str(e)}")
raise HTTPException(
status_code=500,
detail="Internal Server Error",
)
@router.get("/{conversation_id}", response_model=schemas.ChatHistory)
def read_chat_history(
conversation_id: uuid.UUID,
db: Session = Depends(deps.get_db),
current_user: models.User = Security(
deps.get_current_user,
),
) -> schemas.ChatHistory:
"""
Read a chat history by ID
"""
try:
chat_history = crud.chat.get_by_id(db, id=conversation_id)
if chat_history is None or chat_history.user_id != current_user.id:
raise HTTPException(
status_code=404, detail="Chat history not found")
return chat_history
except Exception as e:
print(traceback.format_exc())
logger.error(f"Error reading chat history: {str(e)}")
raise HTTPException(
status_code=500,
detail="Internal Server Error",
)
@router.post("/delete")
def delete_chat_history(
chat_history_in: schemas.ChatDelete,
db: Session = Depends(deps.get_db),
current_user: models.User = Security(
deps.get_current_user,
),
):
"""
Delete a chat history by ID
"""
try:
chat_history_id = chat_history_in.conversation_id
chat_history = crud.chat.get(db, id=chat_history_id)
if chat_history is None or chat_history.user_id != current_user.id:
raise HTTPException(
status_code=404, detail="Chat history not found")
crud.chat.remove(db=db, id=chat_history_id)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"message": "Chat history deleted successfully",
},
)
except Exception as e:
print(traceback.format_exc())
logger.error(f"Error deleting chat history: {str(e)}")
raise HTTPException(
status_code=500,
detail="Internal Server Error",
)

View file

@ -6,4 +6,4 @@ from .subscription_crud import subscription
from .document_crud import documents
from .department_crud import department
from .audit_crud import audit
from .chathistory_crud import chat
from .chat_crud import chat, chat_item

View file

@ -0,0 +1,32 @@
from sqlalchemy.sql.expression import desc, asc
from typing import Optional, List, Union, Dict, Any
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
import uuid
from private_gpt.users.crud.base import CRUDBase
from private_gpt.users.models.chat import ChatHistory, ChatItem
from private_gpt.users.schemas.chat import ChatHistoryCreate, ChatHistoryCreate, ChatItemCreate, ChatItemUpdate
class CRUDChat(CRUDBase[ChatHistory, ChatHistoryCreate, ChatHistoryCreate]):
def get_by_id(self, db: Session, *, id: uuid.UUID) -> Optional[ChatHistory]:
return db.query(self.model).filter(ChatHistory.conversation_id == id).first()
def get_chat_history(
self, db: Session, *, skip: int = 0, limit: int = 100
) -> List[ChatHistory]:
return (
db.query(self.model)
.order_by(desc(getattr(ChatHistory, 'created_at')))
.offset(skip)
.limit(limit)
.all()
)
class CRUDChatItem(CRUDBase[ChatItem, ChatItemCreate, ChatItemUpdate]):
pass
chat = CRUDChat(ChatHistory)
chat_item = CRUDChatItem(ChatItem)

View file

@ -35,9 +35,10 @@ class CRUDDocuments(CRUDBase[Document, DocumentCreate, DocumentUpdate]):
.join(document_department_association)
.join(Department)
.filter(document_department_association.c.department_id == department_id)
.order_by(desc(getattr(Document, 'uploaded_at')))
.offset(skip)
.limit(limit)
.all().order_by(desc(getattr(Document, 'uploaded_at')))
.all()
)
def get_files_to_verify(

View file

@ -7,4 +7,4 @@ from .subscription import Subscription
from .department import Department
from .audit import Audit
from .document_department import document_department_association
from .chat_history import ChatHistory
from .chat import ChatHistory, ChatItem

View file

@ -0,0 +1,71 @@
import uuid
from datetime import datetime
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text, Boolean, event, JSON
from private_gpt.users.db.base_class import Base
class ChatHistory(Base):
"""Models a chat history table"""
__tablename__ = "chat_history"
conversation_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
title = Column(String(255), nullable=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now,
onupdate=datetime.now)
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship("User", back_populates="chat_histories")
chat_items = relationship(
"ChatItem", back_populates="chat_history", cascade="all, delete-orphan")
_title_generated = Column(Boolean, default=False)
def __init__(self, user_id, chat_items=None, **kwargs):
super().__init__(**kwargs)
self.user_id = user_id
self.chat_items = chat_items or []
self.generate_title()
def generate_title(self):
user_chat_items = [
item for item in self.chat_items if item.role == "user"]
if user_chat_items:
first_user_chat_item = user_chat_items[0]
self.title = first_user_chat_item.content[:30]
else:
self.title = str(self.conversation_id)
def __repr__(self):
"""Returns string representation of model instance"""
return f"<ChatHistory {self.conversation_id!r}>"
class ChatItem(Base):
"""Models a chat item table"""
__tablename__ = "chat_items"
id = Column(Integer, nullable=False, primary_key=True)
sender = Column(String(225), nullable=False)
content = Column(JSON, nullable=True)
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now,
onupdate=datetime.now)
like = Column(Boolean, default=True)
conversation_id = Column(UUID(as_uuid=True), ForeignKey(
"chat_history.conversation_id"), nullable=False)
chat_history = relationship("ChatHistory", back_populates="chat_items")
def __repr__(self):
"""Returns string representation of model instance"""
return f"<ChatItem {self.id!r}>"
@event.listens_for(ChatHistory, "after_insert")
def receive_after_insert(mapper, connection, target):
"""Update title after insertion to reflect the conversation_id"""
if not target._title_generated:
target.generate_title()
target._title_generated = True

View file

@ -1,10 +1,25 @@
from .role import Role, RoleCreate, RoleInDB, RoleUpdate
from .token import TokenSchema, TokenPayload
from .user import User, UserCreate, UserInDB, UserUpdate, UserBaseSchema, Profile, UsernameUpdate, DeleteUser, UserAdminUpdate, UserAdmin, PasswordUpdate
from .user import (
User, UserCreate, UserInDB, UserUpdate, UserBaseSchema, Profile,
UsernameUpdate, DeleteUser, UserAdminUpdate, UserAdmin, PasswordUpdate
)
from .user_role import UserRole, UserRoleCreate, UserRoleInDB, UserRoleUpdate
from .subscription import Subscription, SubscriptionBase, SubscriptionCreate, SubscriptionUpdate
from .subscription import (
Subscription, SubscriptionBase, SubscriptionCreate, SubscriptionUpdate
)
from .company import Company, CompanyBase, CompanyCreate, CompanyUpdate
from .documents import Document, DocumentCreate, DocumentsBase, DocumentUpdate, DocumentList, DepartmentList, DocumentEnable, DocumentDepartmentUpdate, DocumentCheckerUpdate, DocumentMakerCreate, DocumentDepartmentList, DocumentView, DocumentVerify, DocumentFilter
from .department import Department, DepartmentCreate, DepartmentUpdate, DepartmentAdminCreate, DepartmentDelete
from .documents import (
Document, DocumentCreate, DocumentsBase, DocumentUpdate, DocumentList,
DepartmentList, DocumentEnable, DocumentDepartmentUpdate, DocumentCheckerUpdate,
DocumentMakerCreate, DocumentDepartmentList, DocumentView, DocumentVerify,
DocumentFilter
)
from .department import (
Department, DepartmentCreate, DepartmentUpdate, DepartmentAdminCreate, DepartmentDelete
)
from .audit import AuditBase, AuditCreate, AuditUpdate, Audit, GetAudit
from .chat_history import Chat, ChatBase, ChatCreate, ChatDelete, ChatUpdate, ChatMessages
from .chat import (
ChatHistory, ChatHistoryBase, ChatHistoryCreate, ChatHistoryUpdate, ChatDelete,
ChatItem, ChatItemBase, ChatItemCreate, ChatItemUpdate, CreateChatHistory
)

View file

@ -0,0 +1,56 @@
from datetime import datetime
from typing import List, Optional, Union, Dict
from pydantic import BaseModel, Json
import uuid
class ChatItemBase(BaseModel):
conversation_id: uuid.UUID
sender: str
content: Union[str, Dict]
class ChatItemCreate(ChatItemBase):
pass
class ChatItemUpdate(ChatItemBase):
like: Optional[bool]
class ChatItem(ChatItemBase):
id: int
created_at: datetime
updated_at: datetime
class Config:
orm_mode = True
class ChatHistoryBase(BaseModel):
user_id: int
title: Optional[str]
class ChatHistoryCreate(ChatHistoryBase):
chat_items: Optional[List[ChatItemCreate]]
class ChatHistoryUpdate(ChatHistoryBase):
updated_at: datetime
chat_items: Optional[List[ChatItemCreate]]
class Chat(BaseModel):
conversation_id: uuid.UUID
class ChatHistory(ChatHistoryBase):
conversation_id: uuid.UUID
created_at: datetime
updated_at: datetime
chat_items: List[ChatItem]
class Config:
orm_mode = True
class ChatDelete(BaseModel):
conversation_id: uuid.UUID
class CreateChatHistory(BaseModel):
user_id: int

View file

@ -53,6 +53,7 @@ gradio = {version ="^4.19.2", optional = true}
aiofiles = "^23.2.1"
timm = "^0.9.16"
fastapi-filter = {extras = ["sqlalchemy"], version = "^1.1.0"}
uuid = "^1.30"
[tool.poetry.extras]
ui = ["gradio"]

View file

@ -10,7 +10,7 @@ from private_gpt.settings.settings import settings
resume_download = True
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='Setup: Download models from huggingface')
parser = argparse.ArgumentParser(prog='Setup: Download models from Hugging Face')
parser.add_argument('--resume', default=True, action=argparse.BooleanOptionalAction, help='Enable/Disable resume_download options to restart the download progress interrupted')
args = parser.parse_args()
resume_download = args.resume
@ -40,10 +40,10 @@ print("LLM model downloaded!")
# Download Tokenizer
print(f"Downloading tokenizer {settings().llm.tokenizer}")
local_path = models_path / "tokenizer/Mistral-7B-Instruct-v0.1"
AutoTokenizer.from_pretrained(
pretrained_model_name_or_path=settings().llm.tokenizer,
cache_dir=models_cache_path,
local_path
)
print("Tokenizer downloaded!")
print("Setup done")
print("Setup done")

View file

@ -8,16 +8,24 @@ llm:
context_window: 3900
tokenizer: mistralai/Mistral-7B-Instruct-v0.2
llamacpp:
prompt_style: "mistral"
llm_hf_repo_id: TheBloke/Mistral-7B-Instruct-v0.2-GGUF
llm_hf_model_file: mistral-7b-instruct-v0.2.Q4_K_M.gguf
prompt_style: "chatml"
llm_hf_repo_id: TheBloke/OpenHermes-2.5-Mistral-7B-GGUF
llm_hf_model_file: openhermes-2.5-mistral-7b.Q5_K_M.gguf
tfs_z: 1.0 # Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting
top_k: 40 # Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)
top_p: 0.9 # Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)
repeat_last_n: 64 # Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)
repeat_penalty: 1.1 # Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)
embedding:
# Should be matching the value above in most cases
mode: huggingface
ingest_mode: simple
huggingface:
embedding_hf_model_name: BAAI/bge-small-en-v1.5
embedding_hf_model_name: BAAI/bge-large-en-v1.5
vectorstore:
database: qdrant

View file

@ -59,7 +59,7 @@ embedding:
ingest_mode: simple
huggingface:
embedding_hf_model_name: BAAI/bge-large-en-v1.5
embedding_hf_model_name: mixedbread-ai/mxbai-embed-large-v1
vectorstore:
database: qdrant