dhruv4023 commited on
Commit
a3386d3
1 Parent(s): fe55b83

Synced repo using 'sync_with_huggingface' Github Action

Browse files
main.py CHANGED
@@ -1,20 +1,15 @@
1
- from pydantic import BaseModel
2
- from typing import List, Optional
3
- from fastapi.responses import JSONResponse
4
- from starlette.middleware import Middleware
5
  from starlette.middleware.gzip import GZipMiddleware
6
  from starlette.middleware.cors import CORSMiddleware
7
- from fastapi import FastAPI, File, UploadFile, Depends, Form, BackgroundTasks
8
 
9
- from OtherFun import *
10
- from Middleware import Main
11
- from verifyToken import verify_token_and_role
12
  # import os
13
- origins = ["https://chatbotservernode.onrender.com","https://cbns.vercel.app", "https://hfhchatbot.vercel.app", "http://localhost:5000", "http://localhost:3000", "https://localhost:5000"]
14
 
15
  # origins = os.getenv("ALLOWED_ORIGINS", "").split(",")
16
-
17
- app = FastAPI(debug=True)
18
 
19
  app.add_middleware(GZipMiddleware)
20
  app.add_middleware(
@@ -22,65 +17,12 @@ app.add_middleware(
22
  allow_origins=origins, # You can replace '*' with specific origins
23
  allow_credentials=True,
24
  allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], # or specific methods
25
- allow_headers=["Authorization", "Content-Type", "Accept"] # or specific headers
26
  )
27
 
28
- model = Main()
29
-
30
- class BodyModel(BaseModel):
31
- query: str
32
- chain_name: Optional[str] = None # Made chain_name optional
33
-
34
- @app.get("/")
35
- async def home():
36
- return "chatbot api server is running..."
37
-
38
-
39
- @app.post("/ask")
40
- async def askQ(body: BodyModel, token: str = Depends(verify_token_and_role)):
41
- try:
42
- response = model.ask_question(body.query, token["username"] if body.chain_name is None else body.chain_name)
43
- return JSONResponse(content={"success": True, "data": response})
44
- except Exception as e: # Catch specific exceptions
45
- return JSONResponse(content={"success": False, "error": str(e)})
46
-
47
-
48
- @app.post("/create/embedding")
49
- async def createEmbedding(collection_name: str = Form(...), files: List[UploadFile] = File(None), token: str = Depends(verify_token_and_role)):
50
- try:
51
- if not files:
52
- return JSONResponse(content={"success": False, "error":"No files provided"})
53
-
54
- responses = []
55
- for file in files:
56
- response = await process_file(model, collection_name, file)
57
- responses.append(response)
58
-
59
- return JSONResponse(content={"success": True,"responses": responses})
60
- except Exception as e:
61
- return JSONResponse(content={"success": False, "error": str(e)})
62
-
63
-
64
- @app.post("/create/tmp/chain")
65
- async def createTmpChain(background_tasks: BackgroundTasks, files: List[UploadFile] = File(...), token: str = Depends(verify_token_and_role)):
66
- try:
67
- if not files:
68
- return JSONResponse(content={"success": False, "error":"No files provided"})
69
-
70
- all_contents = b""
71
- for file in files:
72
- contents = await file.read()
73
- all_contents += contents
74
 
75
- file_extension = files[0].filename.split(".")[-1]
76
- if file_extension == "pdf":
77
- chain_name = token["username"]
78
- model.generate_tmp_embedding_and_chain(all_contents, chain_name)
79
- background_tasks.add_task(delete_chain_after_delay(model, chain_name))
80
- return JSONResponse(content={"success": True, "message": "Chain created. Will be deleted after 2 hours."})
81
- elif file_extension == "txt":
82
- all_contents.decode("utf-8")
83
- return JSONResponse(content={"success": False, "error": "Unsupported file format"})
84
- except Exception as e:
85
- return JSONResponse(content={"success": False, "error": str(e)})
86
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from src.config.appConfig import ENV_VAR
 
 
3
  from starlette.middleware.gzip import GZipMiddleware
4
  from starlette.middleware.cors import CORSMiddleware
 
5
 
6
+ from src.main import main
7
+
 
8
  # import os
9
+ origins = ["http://localhost:3000"]
10
 
11
  # origins = os.getenv("ALLOWED_ORIGINS", "").split(",")
12
+ app = FastAPI(debug=ENV_VAR.DEBUG)
 
13
 
14
  app.add_middleware(GZipMiddleware)
15
  app.add_middleware(
 
17
  allow_origins=origins, # You can replace '*' with specific origins
18
  allow_credentials=True,
19
  allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], # or specific methods
20
+ allow_headers=["Authorization", "Content-Type", "Accept"], # or specific headers
21
  )
22
 
23
+ app.include_router(main)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ import uvicorn
 
 
 
 
 
 
 
 
 
 
26
 
27
+ if __name__ == "__main__":
28
+ uvicorn.run("main:app", host="0.0.0.0", port=5000, reload=ENV_VAR.DEBUG)
requirements.txt CHANGED
@@ -1,5 +1,7 @@
1
  langchain==0.1.8
2
  python-dotenv==1.0.0
 
 
3
 
4
  # pdf
5
  PyPDF2==3.0.1
@@ -15,13 +17,16 @@ faiss-cpu
15
  # mongodb
16
  pymongo==4.6.1
17
 
 
 
18
  # API-END point
19
  fastapi==0.109.2
20
  fastapi-cors
21
  uvicorn[standard]==0.17.*
22
  python-multipart==0.0.9
23
  PyJWT==2.8.0
 
24
 
25
 
26
- huggingface_hub
27
- typing
 
1
  langchain==0.1.8
2
  python-dotenv==1.0.0
3
+ huggingface_hub
4
+ typing
5
 
6
  # pdf
7
  PyPDF2==3.0.1
 
17
  # mongodb
18
  pymongo==4.6.1
19
 
20
+ cloudinary
21
+
22
  # API-END point
23
  fastapi==0.109.2
24
  fastapi-cors
25
  uvicorn[standard]==0.17.*
26
  python-multipart==0.0.9
27
  PyJWT==2.8.0
28
+ pydantic
29
 
30
 
31
+
32
+ # requests-toolbelt
src/bot/MongoChainGenerator.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.config.appConfig import ENV_VAR, CONST_VAR, LOG
2
+ from langchain.chains import RetrievalQA
3
+ from langchain.prompts import PromptTemplate
4
+ from langchain.llms.huggingface_endpoint import HuggingFaceEndpoint
5
+ from langchain.vectorstores.mongodb_atlas import MongoDBAtlasVectorSearch
6
+ from langchain.vectorstores.faiss import FAISS
7
+ from huggingface_hub import login
8
+
9
+ login(
10
+ token=ENV_VAR.HUGGINGFACEHUB_API_TOKEN,
11
+ write_permission=True,
12
+ add_to_git_credential=True,
13
+ )
14
+
15
+
16
+ class MongoChainGenerator:
17
+ LLM = None
18
+
19
+ def __init__(
20
+ self,
21
+ embedding_model,
22
+ template_context,
23
+ db_collection_name=None,
24
+ tmp_vector_embedding=None,
25
+ ):
26
+ if db_collection_name:
27
+ self._load_vectors(embedding_model, db_collection_name)
28
+ else:
29
+ self._create_tmp_retriever(tmp_vector_embedding)
30
+
31
+ self._initialize_prompt(template_context)
32
+
33
+ if MongoChainGenerator.LLM is None:
34
+ self._initialize_llm()
35
+
36
+ def _create_tmp_retriever(self, tmp_vector_embedding: FAISS):
37
+ self.qa_retriever = tmp_vector_embedding.as_retriever(
38
+ search_type="similarity", search_kwargs={"k": 7}
39
+ )
40
+ LOG.debug("Temporary retriever created")
41
+
42
+ def _load_vectors(self, embedding_model, db_collection_name):
43
+ self.qa_retriever = MongoDBAtlasVectorSearch.from_connection_string(
44
+ connection_string=ENV_VAR.MONGO_DB_URL,
45
+ namespace=ENV_VAR.MONGO_DB_NAME + "." + db_collection_name,
46
+ embedding=embedding_model,
47
+ ).as_retriever(search_type="similarity", search_kwargs={"k": 7})
48
+ LOG.debug("Retriever loaded from MongoDB Atlas")
49
+
50
+ def _initialize_prompt(self, template_context):
51
+ template = (
52
+ template_context
53
+ + """
54
+ {context}
55
+
56
+ Question: {question} all related details.
57
+ Answer:"""
58
+ )
59
+ self.prompt = PromptTemplate(
60
+ template=template, input_variables=["context", "question"]
61
+ )
62
+ LOG.debug("Prompt template initialized")
63
+
64
+ def _initialize_llm(self):
65
+ MongoChainGenerator.LLM = HuggingFaceEndpoint(
66
+ repo_id=CONST_VAR.TEXT_GENERATOR_MODEL_REPO_ID,
67
+ temperature=0.8,
68
+ max_new_tokens=4096,
69
+ )
70
+ # MongoChainGenerator.LLM = HuggingFaceHub(repo_id=CONST_VAR.TEXT_GENERATOR_MODEL_REPO_ID, model_kwargs={"temperature": 0.85, "return_full_text": False, "max_length": 4096, "max_new_tokens": 4096})
71
+ LOG.info("LLM initialized")
72
+
73
+ def generate_retrieval_qa_chain(self):
74
+ chain = RetrievalQA.from_chain_type(
75
+ llm=MongoChainGenerator.LLM,
76
+ retriever=self.qa_retriever,
77
+ chain_type_kwargs={"prompt": self.prompt},
78
+ )
79
+ LOG.debug("Retrieval QA chain generated")
80
+ return chain
src/bot/MongoEmbeddingGenerator.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from io import BytesIO
2
+ import PyPDF2
3
+ from src.config.appConfig import *
4
+ from src.config.databaseConfig import DATABASE
5
+
6
+ from langchain.vectorstores.faiss import FAISS
7
+ from langchain.vectorstores.mongodb_atlas import MongoDBAtlasVectorSearch
8
+ from langchain.embeddings.huggingface_hub import HuggingFaceHubEmbeddings
9
+
10
+ class MongoEmbeddingGenerator:
11
+
12
+ def __init__(self, repo_id):
13
+ self.embedding_model = HuggingFaceHubEmbeddings(repo_id=repo_id, huggingfacehub_api_token=ENV_VAR.HUGGINGFACEHUB_API_TOKEN)
14
+ LOG.info("Embedding model initialised")
15
+
16
+ def _extract_text_from_pdf(self, pdf_bytes):
17
+ pdf_file = BytesIO(pdf_bytes)
18
+ pdf_reader = PyPDF2.PdfReader(pdf_file)
19
+ return [pdf_reader.pages[page_num].extract_text() for page_num in range(len(pdf_reader.pages))]
20
+
21
+ def generate_tmp_embeddings(self, pdf_bytes):
22
+ texts = self._extract_text_from_pdf(pdf_bytes)
23
+ return FAISS.from_texts(texts=texts, embedding=self.embedding_model)
24
+
25
+ def generate_embeddings(self, pdf_bytes, file_name: str, collection_name: str):
26
+ client = DATABASE.client
27
+ if client[ENV_VAR.MONGO_DB_NAME_CACHE][collection_name].find_one({"src_file_name": file_name}):
28
+ LOG.debug(f"Vectors already exist in MongoDB for file {file_name}")
29
+ return f"Vectors already exist in MongoDB for file {file_name}"
30
+ else:
31
+ texts = self._extract_text_from_pdf(pdf_bytes)
32
+ client[ENV_VAR.MONGO_DB_NAME_CACHE][collection_name].insert_one({"src_file_name": file_name})
33
+ MongoDBAtlasVectorSearch.from_texts(texts=texts, embedding=self.embedding_model, collection=client[ENV_VAR.MONGO_DB_NAME][collection_name])
34
+ LOG.debug(f"Vectors stored in MongoDB for file {file_name}")
35
+ return f"Vectors stored in MongoDB for file {file_name}"
src/bot/OtherFun.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import UploadFile
2
+ import asyncio
3
+ from src.bot.main import Main
4
+ from src.config.appConfig import LOG
5
+
6
+
7
+ def delete_chain_after_delay(model: Main, chain_name: str):
8
+ async def delete_chain():
9
+ try:
10
+ await asyncio.sleep(7200) # Sleep for 2 hours
11
+ if chain_name in model.qa_chains:
12
+ del model.qa_chains[chain_name]
13
+ # Log deletion
14
+ LOG.info(f"Chain '{chain_name}' deleted after 2 hours")
15
+ except Exception as e:
16
+ LOG.error(f"An error occurred while deleting chain '{chain_name}': {e}")
17
+
18
+ return delete_chain
19
+
20
+
21
+ async def process_file(model: Main, collection_name: str, file: UploadFile):
22
+ try:
23
+ contents = await file.read()
24
+
25
+ file_extension = file.filename.split(".")[-1]
26
+
27
+ if file_extension == "pdf":
28
+ response = model.generate_embedding(
29
+ contents, file.filename, collection_name)
30
+ elif file_extension == "txt":
31
+ response = contents.decode("utf-8")
32
+ else:
33
+ raise ValueError(f"Unsupported file format for {file.filename}")
34
+
35
+ return response
36
+ except Exception as e:
37
+ LOG.error(f"An error occurred while processing file '{file.filename}': {e}")
38
+ return f"Error processing file '{file.filename}': {e}"
src/bot/main.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.bot.MongoChainGenerator import *
2
+ from src.bot.MongoEmbeddingGenerator import *
3
+
4
+ from src.config.appConfig import LOG
5
+ from src.config.databaseConfig import DATABASE
6
+
7
+
8
+ class Main:
9
+ qa_chains = {}
10
+ embedding_generator = None
11
+
12
+ def __init__(self) -> None:
13
+ DATABASE()
14
+ self._initialize_embedding_generator()
15
+ self._load_existing_qa_chains()
16
+
17
+ def _initialize_embedding_generator(self):
18
+ if Main.embedding_generator is None:
19
+ Main.embedding_generator = MongoEmbeddingGenerator(repo_id=CONST_VAR.EMBEDDING_MODEL_REPO_ID)
20
+ LOG.debug("Embedding generator initialized")
21
+
22
+ def _load_existing_qa_chains(self):
23
+ chats = DATABASE.client["chatData"]["chats"].find()
24
+ for chat in chats:
25
+ if chat["collectionName"] not in Main.qa_chains:
26
+ self.create_exist_chains(chat)
27
+
28
+ def create_exist_chains(self, chat):
29
+ if chat["collectionName"] not in Main.qa_chains:
30
+ qa_generator = MongoChainGenerator(
31
+ embedding_model=Main.embedding_generator.embedding_model,
32
+ db_collection_name=chat["collectionName"],
33
+ template_context=chat["templateContext"]
34
+ )
35
+ Main.qa_chains[chat["collectionName"]] = qa_generator.generate_retrieval_qa_chain()
36
+ LOG.debug("Chain created for collection " + chat["collectionName"])
37
+ else:
38
+ LOG.debug("Chain already exists for collection " + chat["collectionName"])
39
+
40
+ def generate_embedding(self, content: str, file_name: str, collection_name: str):
41
+ return Main.embedding_generator.generate_embeddings(content, file_name, collection_name)
42
+
43
+ def generate_tmp_embedding_and_chain(self, contents: str, tmp_collection_name):
44
+ qa_generator = MongoChainGenerator(
45
+ embedding_model=Main.embedding_generator.embedding_model,
46
+ template_context=CONST_VAR.TEMPLATE_CONTEXT,
47
+ tmp_vector_embedding=Main.embedding_generator.generate_tmp_embeddings(pdf_bytes=contents)
48
+ )
49
+ Main.qa_chains[tmp_collection_name] = qa_generator.generate_retrieval_qa_chain()
50
+ LOG.debug(tmp_collection_name + ' chain created')
51
+
52
+ def ask_question(self, question: str, collection_name):
53
+ if collection_name in Main.qa_chains:
54
+ try:
55
+ LOG.debug(collection_name + " answering")
56
+ response = Main.qa_chains[collection_name]({"query": question, "early_stopping": True, "min_length": 2000, "max_tokens": 5000})
57
+ return response["result"]
58
+ except Exception as e:
59
+ LOG.error("An error occurred while answering question: {}".format(str(e)))
60
+ return "Retry to ask question! An error occurred: {}".format(str(e))
61
+ else:
62
+ LOG.warning("Chain for collection '{}' not found.".format(collection_name))
63
+ return "Chain for collection '{}' not found.".format(collection_name)
64
+
65
+ def check_collection_name(self, collection_name):
66
+ return collection_name in self.qa_chains
src/config/appConfig.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ class CLOUDINARY():
8
+ CLOUDINARY_CLOUD_NAME = os.environ.get("CLOUDINARY_CLOUD_NAME")
9
+ CLOUDINARY_API_KEY = os.environ.get("CLOUDINARY_API_KEY")
10
+ CLOUDINARY_API_SECRET = os.environ.get("CLOUDINARY_API_SECRET")
11
+
12
+ class ENV_VAR():
13
+ MONGO_DB_URL = os.environ.get("MONGO_DB_URL")
14
+ MONGO_DB_NAME = os.environ.get("MONGO_DB_NAME")
15
+ MONGO_DB_NAME_CHATS = os.environ.get("MONGO_DB_NAME_CHATS")
16
+ HUGGINGFACEHUB_API_TOKEN = os.environ.get("HUGGINGFACEHUB_API_TOKEN")
17
+ MONGO_DB_NAME_CACHE = os.environ.get("MONGO_DB_NAME_CACHE")
18
+ JWT_SECRET = os.environ.get("JWT_SECRET")
19
+ AUTH_API_END = os.environ.get("AUTH_API_END")
20
+ DEBUG = (
21
+ os.environ.get("DEBUG").lower() == "true" if os.environ.get("DEBUG") else False
22
+ )
23
+
24
+ class CONST_VAR():
25
+ TEXT_GENERATOR_MODEL_REPO_ID = "mistralai/Mixtral-8x7B-Instruct-v0.1"
26
+ EMBEDDING_MODEL_REPO_ID = "sentence-transformers/all-MiniLM-L6-v2"
27
+ TEMPLATE_CONTEXT = """
28
+ Use the following pieces of context to answer the question at the end.
29
+ You should prefer information which are more related to asked question.
30
+ Make sure to rely on information from text only and not on questions to provide accurate responses.
31
+ When you find particular answer in given text, display its context useful, make sure to cite it in the your answer.
32
+ If you don't know the answer, just say that you don't know, don't try to make up an answer.
33
+ You can only use the given to you to answer the question.
34
+ Generate concise answers and relevant data related to the asked question.
35
+ You must represent the answer in proper format such as make points highlight some major information.
36
+ don't attach your created quetions. if you don't get answer from the given text just say i don't know and terminate answering.
37
+ if you get answer from the text than write all about the asked quetion and relevant data related to it.
38
+ don't use your own knowledge just use the provided text to answer the question.
39
+ """
40
+
41
+
42
+ class LOG:
43
+ def __init__(self) -> None:
44
+ pass
45
+
46
+ @staticmethod
47
+ def configure_logging(level=logging.INFO):
48
+ logging.basicConfig(level=level) # Set the logging level
49
+
50
+ @staticmethod
51
+ def debug(msg):
52
+ logging.debug(msg)
53
+
54
+ @staticmethod
55
+ def info(msg):
56
+ logging.info(msg)
57
+
58
+ @staticmethod
59
+ def warning(msg):
60
+ logging.warning(msg)
61
+
62
+ @staticmethod
63
+ def error(msg):
64
+ logging.error(msg)
65
+
66
+ @staticmethod
67
+ def critical(msg):
68
+ logging.critical(msg)
69
+
70
+
71
+ if ENV_VAR.DEBUG:
72
+ LOG.configure_logging(logging.DEBUG)
src/config/cloudinaryConfig.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import cloudinary
2
+ from src.config.appConfig import CLOUDINARY
3
+
4
+ cloudinary.config(
5
+ cloud_name=CLOUDINARY.CLOUDINARY_CLOUD_NAME,
6
+ api_key=CLOUDINARY.CLOUDINARY_API_KEY,
7
+ api_secret=CLOUDINARY.CLOUDINARY_API_SECRET,
8
+ )
src/config/databaseConfig.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.config.appConfig import *
2
+ from pymongo import MongoClient
3
+
4
+
5
+ class DATABASE:
6
+ client = None
7
+ collections=None
8
+ def __init__(self):
9
+ self._initialize_mongodb_client()
10
+
11
+ def _initialize_mongodb_client(self):
12
+ if DATABASE.client is None:
13
+ DATABASE.client = MongoClient(ENV_VAR.MONGO_DB_URL)
14
+ print("mongodb connected")
src/controllers/auth/auth_controller.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Response, Request, UploadFile
2
+ from src.config.appConfig import ENV_VAR
3
+ from src.helpers.response import ResponseHandler
4
+ from src.helpers.send_request import sendRequest
5
+ from typing import Optional
6
+
7
+ router = APIRouter()
8
+ AUTH_API_END = ENV_VAR.AUTH_API_END
9
+
10
+
11
+ @router.post("/register/")
12
+ async def register(req: Request, picPath: UploadFile = None):
13
+ try:
14
+ form_data = await req.form()
15
+
16
+ payload = {key: form_data[key] for key in form_data if key != "picPath"}
17
+
18
+ files = None
19
+
20
+ if picPath:
21
+ files = {"picPath": (picPath.filename, picPath.file, picPath.content_type)}
22
+
23
+ # else f"{AUTH_API_END}/api/v1/auth/register/", json=payload}
24
+ response = sendRequest(
25
+ f"{AUTH_API_END}/api/v1/auth/register/", "post", payload, files
26
+ )
27
+
28
+ return ResponseHandler.success_mediator(response)
29
+ except Exception as e:
30
+ return ResponseHandler.error(9999, e)
31
+
32
+
33
+ @router.get("/get/usernames")
34
+ async def get_user_names():
35
+ try:
36
+ response = sendRequest(f"{AUTH_API_END}/api/v1/auth/get/usernames")
37
+ return ResponseHandler.success_mediator(response)
38
+ except Exception as e:
39
+ return ResponseHandler.error(9999, e)
40
+
41
+
42
+ @router.post("/login/")
43
+ async def login_control(req: dict):
44
+ try:
45
+ response = sendRequest(f"{AUTH_API_END}/api/v1/auth/login/", "post", req)
46
+ return ResponseHandler.success_mediator(response)
47
+ except Exception as e:
48
+ return ResponseHandler.error(9999, e)
49
+
50
+
51
+ @router.post("/change/password/")
52
+ async def change_pass_control(req: dict, authorization: str = None):
53
+ try:
54
+ headers = {"Content-Type": "application/json"}
55
+ if authorization:
56
+ headers["Authorization"] = authorization
57
+
58
+ response = sendRequest(
59
+ f"{AUTH_API_END}/api/v1/auth/change/password", "post", req, None, headers
60
+ )
61
+ return ResponseHandler.success_mediator(response)
62
+ except Exception as e:
63
+ return ResponseHandler.error(9999, e)
src/controllers/auth/otp_controller.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request
2
+ from src.config.appConfig import ENV_VAR
3
+ from src.helpers.response import ResponseHandler
4
+ from src.helpers.send_request import sendRequest
5
+
6
+ router = APIRouter()
7
+ AUTH_API_END = ENV_VAR.AUTH_API_END
8
+
9
+
10
+ @router.post("/send-otp")
11
+ async def send_otp_controller(req: Request):
12
+ try:
13
+ # Make the request to the authentication API endpoint
14
+ response = sendRequest(
15
+ f"{AUTH_API_END}/api/v1/mail/send-otp",
16
+ "post",
17
+ req,
18
+ None,
19
+ {"Content-Type": "application/json"},
20
+ )
21
+ # Handle the response using the ResponseHandler
22
+ return ResponseHandler.success_mediator(response)
23
+ except Exception as e:
24
+ # Log and handle any exceptions
25
+ print(e)
26
+ return ResponseHandler.error(9999, e)
src/controllers/auth/user_controller.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request, UploadFile, Header
2
+ from src.config.appConfig import ENV_VAR
3
+ from src.helpers.response import ResponseHandler
4
+ from src.helpers.send_request import sendRequest
5
+ from typing import Optional
6
+
7
+ router = APIRouter()
8
+ AUTH_API_END = ENV_VAR.AUTH_API_END
9
+
10
+
11
+ @router.get("/userid/{uid}")
12
+ async def get_users(uid: str):
13
+ try:
14
+ response = get_user_date(uid)
15
+ return ResponseHandler.success_mediator(response)
16
+ except Exception as error:
17
+ return ResponseHandler.error(error)
18
+
19
+ def get_user_date(uid):
20
+ return sendRequest(
21
+ url=f"{AUTH_API_END}/api/v1/user/get/userid/{uid}",
22
+ headers={"Content-Type": "application/json"},
23
+ )
24
+
25
+ @router.put("/update/")
26
+ async def update_user_data(
27
+ req: Request, picPath: UploadFile = None, authorization: str = Header(None)
28
+ ):
29
+ try:
30
+ form_data = await req.form()
31
+
32
+ payload = {key: form_data[key] for key in form_data if key != "picPath"}
33
+
34
+ files = None
35
+
36
+ if picPath:
37
+ files = {"picPath": (picPath.filename, picPath.file, picPath.content_type)}
38
+
39
+ headers = {"Authorization": authorization}
40
+ response = sendRequest(
41
+ f"{AUTH_API_END}/api/v1/user/update/",
42
+ "put",
43
+ payload,
44
+ files,
45
+ headers,
46
+ )
47
+ return ResponseHandler.success_mediator(response)
48
+ except Exception as e:
49
+ return ResponseHandler.error(9999, e)
src/controllers/chat/bot_controller.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import BackgroundTasks, APIRouter, Request, UploadFile, Depends, File
2
+ from typing import List
3
+
4
+ from src.helpers.response import ResponseHandler
5
+ from src.helpers.json_convertor import convert_to_json
6
+
7
+ from src.middleware.verifyToken import verify_token
8
+ from src.services.message_services import save_question_and_answer_to_chat_history
9
+
10
+ from src.bot.main import Main
11
+ from src.bot.OtherFun import delete_chain_after_delay, process_file
12
+
13
+ router = APIRouter()
14
+
15
+ model = Main()
16
+
17
+
18
+ @router.post("/ask-question")
19
+ async def askQ(req: Request, token: str = Depends(verify_token)):
20
+ try:
21
+ form_data = await req.json()
22
+ answer = model.ask_question(
23
+ form_data["query"],
24
+ (
25
+ token["username"]
26
+ if form_data["collectionName"] is None
27
+ else form_data["collectionName"]
28
+ ),
29
+ )
30
+ await save_question_and_answer_to_chat_history(
31
+ token["username"],
32
+ {
33
+ "question": form_data["query"],
34
+ "answer": answer,
35
+ "collectionName": (
36
+ form_data["query"] if form_data["query"] else "CHAT WITH YOUR PDF"
37
+ ),
38
+ },
39
+ )
40
+ return ResponseHandler.success(2001, answer)
41
+ except Exception as error:
42
+ print(error)
43
+ return ResponseHandler.error(9999, error, 500)
44
+
45
+
46
+ @router.post("/create/embedding/{collection_name}")
47
+ async def createEmbedding(
48
+ collection_name: str,
49
+ files: List[UploadFile] = File(None),
50
+ tokenData: str = Depends(verify_token),
51
+ ):
52
+ try:
53
+ if not files:
54
+ return ResponseHandler.error(2003)
55
+
56
+ responses = []
57
+ for file in files:
58
+ response = process_file(model, collection_name, file)
59
+ responses.append(response)
60
+
61
+ return ResponseHandler.success(2002, response)
62
+ except Exception as error:
63
+ return ResponseHandler.error(9999, error, 500)
64
+
65
+
66
+ @router.post("/create/tmp/chain")
67
+ async def createTmpChain(
68
+ background_tasks: BackgroundTasks,
69
+ files: List[UploadFile] = File(...),
70
+ tokenData: str = Depends(verify_token),
71
+ ):
72
+ try:
73
+ if not files:
74
+ return ResponseHandler.error(2003, error, 500)
75
+
76
+ all_contents = b""
77
+ for file in files:
78
+ contents = await file.read()
79
+ all_contents += contents
80
+
81
+ file_extension = files[0].filename.split(".")[-1]
82
+ if file_extension == "pdf":
83
+ chain_name = tokenData["username"]
84
+ model.generate_tmp_embedding_and_chain(all_contents, chain_name)
85
+ background_tasks.add_task(delete_chain_after_delay(model, chain_name))
86
+ return ResponseHandler.success(2001)
87
+ elif file_extension == "txt":
88
+ all_contents.decode("utf-8")
89
+ return ResponseHandler.error(2004, error, 500)
90
+
91
+ except Exception as error:
92
+ return ResponseHandler.error(9999, error, 500)
src/controllers/chat/chats_controller.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Depends, Request, UploadFile
2
+
3
+ from src.config.databaseConfig import DATABASE
4
+ from src.config.appConfig import ENV_VAR
5
+
6
+ from src.helpers.pagination import *
7
+ from src.helpers.response import ResponseHandler
8
+ from src.helpers.upload_file_cloudinary import upload_file
9
+
10
+ from src.middleware.verifyToken import verify_token
11
+
12
+ router = APIRouter()
13
+
14
+ # Your route definitions
15
+
16
+
17
+ from typing import Dict, List
18
+
19
+
20
+ def convert_form_data_to_dict(form_data) -> Dict[str, List[str]]:
21
+ data_dict = {}
22
+
23
+ for key, value in form_data.items():
24
+ if isinstance(value, str):
25
+ if key in data_dict:
26
+ if isinstance(data_dict[key], list):
27
+ data_dict[key].append(value)
28
+ else:
29
+ data_dict[key] = [data_dict[key], value]
30
+ else:
31
+ data_dict[key] = value
32
+ elif isinstance(value, UploadFile):
33
+ # Handle UploadFile separately if needed
34
+ pass
35
+ else:
36
+ # Handle other types of values if needed
37
+ pass
38
+
39
+ # Convert keys with multiple values to lists
40
+ for key in data_dict:
41
+ if isinstance(data_dict[key], list) and len(data_dict[key]) > 1:
42
+ continue # Skip if it's already a list with multiple values
43
+ elif key in form_data.getlist():
44
+ data_dict[key] = form_data.getlist(key)
45
+
46
+ return data_dict
47
+
48
+
49
+ @router.post("/create")
50
+ async def create_chat(
51
+ req: Request, icon: UploadFile = None, tokenData=Depends(verify_token)
52
+ ):
53
+ try:
54
+ username = tokenData["username"]
55
+ form_data = await req.form()
56
+ # payload = {key: form_data[key] for key in form_data if key != "picPath"}
57
+ # dt = {}
58
+ # print(convert_form_data_to_dict(form_data))
59
+
60
+ if icon is None:
61
+ return ResponseHandler.error(3007, None, 400)
62
+
63
+ file_data = await upload_file(
64
+ icon, form_data["collectionName"] + "_icon", "ChatIcons/"
65
+ )
66
+
67
+ icon_public_id = file_data["public_id"]
68
+ # print(icon_public_id)
69
+ # print(form_data["title"])
70
+ # print(form_data["templateContext"])
71
+ # print(form_data["collectionName"])
72
+ # print(form_data["sampleQuetions"])
73
+ # print(form_data)
74
+ # print(ENV_VAR.MONGO_DB_NAME_CHATS)
75
+
76
+ chat = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS]["chats"].insert_one(
77
+ {
78
+ "username": username,
79
+ "title": form_data["title"],
80
+ "templateContext": form_data["templateContext"],
81
+ "collectionName": form_data["collectionName"],
82
+ "sampleQuetions": (
83
+ (form_data["sampleQuetions"])
84
+ if form_data["sampleQuetions"] is not None
85
+ else []
86
+ ),
87
+ "buttonIcon": icon_public_id,
88
+ }
89
+ )
90
+
91
+ return ResponseHandler.success(3000, chat.acknowledged)
92
+ except Exception as error:
93
+ print(error)
94
+ return ResponseHandler.error(9999, error, 500)
95
+
96
+
97
+ @router.get("/get")
98
+ async def get_paginated_chats(req: Request):
99
+ try:
100
+ page = int(req.query_params.get("page", 1))
101
+ limit = int(req.query_params.get("limit", 10))
102
+
103
+ total_count = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS][
104
+ "chats"
105
+ ].estimated_document_count()
106
+
107
+ chats = (
108
+ DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS]["chats"]
109
+ .find()
110
+ .skip((page - 1) * limit)
111
+ .limit(limit)
112
+ )
113
+
114
+ paginated_response = get_paginated_response(
115
+ list(chats), page, limit, total_count
116
+ )
117
+ # print(paginated_response)
118
+ return ResponseHandler.success(3001, paginated_response)
119
+ except Exception as error:
120
+ return ResponseHandler.error(9000, 500, error)
121
+
122
+
123
+ @router.get("/get/{collection_name}")
124
+ async def get_chat_by_collection_name(req: Request, collection_name: str):
125
+ try:
126
+ chat = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS]["chats"].find_one(
127
+ {"collectionName": collection_name}
128
+ )
129
+ if not chat:
130
+ return ResponseHandler.error(3003, 404)
131
+ return ResponseHandler.success(3002, chat)
132
+ except Exception as error:
133
+ return ResponseHandler.error(9000, 500, error)
134
+
135
+
136
+ @router.put("/edit/{collection_name}")
137
+ async def update_chat(
138
+ req: Request,
139
+ collection_name: str,
140
+ icon: UploadFile = None,
141
+ tokenData=Depends(verify_token),
142
+ ):
143
+ try:
144
+ username = tokenData["username"]
145
+ form_data = await req.form()
146
+ chat = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS]["chats"].find_one(
147
+ {"collectionName": collection_name, "username": username}
148
+ )
149
+
150
+ if not chat:
151
+ return ResponseHandler.error(3003, 404)
152
+
153
+ icon_public_id = None
154
+ if icon is not None:
155
+ file_data = await upload_file(
156
+ icon, form_data["collectionName"] + "_icon", "ChatIcons/"
157
+ )
158
+ icon_public_id = file_data["public_id"]
159
+
160
+ updated_chat = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS][
161
+ "chats"
162
+ ].find_one_and_update(
163
+ {"collectionName": collection_name},
164
+ {
165
+ "$set": {
166
+ "title": form_data["title"],
167
+ "templateContext": form_data["templateContext"],
168
+ "buttonIcon": icon_public_id or chat.get("buttonIcon"),
169
+ }
170
+ },
171
+ )
172
+
173
+ if not updated_chat:
174
+ return ResponseHandler.error(3003, 404)
175
+ return ResponseHandler.success(3004)
176
+ except Exception as error:
177
+ return ResponseHandler.error(9000, 500, error)
178
+
179
+
180
+ @router.delete("/delete/{collection_name}")
181
+ async def delete_chat(collection_name: str, tokenData=Depends(verify_token)):
182
+ try:
183
+ username = tokenData["username"]
184
+ deleted_chat = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS][
185
+ "chats"
186
+ ].find_one_and_delete({"collectionName": collection_name, "username": username})
187
+ if not deleted_chat:
188
+ return ResponseHandler.error(3003, 404)
189
+ return ResponseHandler.success(3005)
190
+ except Exception as error:
191
+ return ResponseHandler.error(9000, 500, error)
src/controllers/chat/history_controller.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ from fastapi import APIRouter, Depends
3
+ from bson import ObjectId
4
+
5
+ from src.config.appConfig import ENV_VAR
6
+ from src.config.databaseConfig import DATABASE
7
+
8
+ from src.helpers.response import ResponseHandler
9
+ from src.helpers.pagination import get_paginated_response
10
+
11
+ from src.middleware.verifyToken import verify_token
12
+
13
+ router = APIRouter()
14
+
15
+
16
+ @router.delete("/delete")
17
+ async def delete_chat_history(tokenData: str = Depends(verify_token)):
18
+ try:
19
+ username = tokenData["username"]
20
+ deleted_chat_history = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS][
21
+ "chathistories"
22
+ ].delete_one({"username": username})
23
+
24
+ if deleted_chat_history.deleted_count == 0:
25
+ return ResponseHandler.error(4002, 404)
26
+ else:
27
+ return ResponseHandler.success(4004)
28
+ except Exception as error:
29
+ print("Error deleting chat history:", error)
30
+ return ResponseHandler.error(9000, 500, error)
31
+
32
+
33
+ @router.delete("/delete/question/{questionId}")
34
+ async def delete_question_from_history(
35
+ questionId: str, tokenData: str = Depends(verify_token)
36
+ ):
37
+ try:
38
+ username = tokenData["username"]
39
+ update_query = {
40
+ "$pull": {"history": {"_id": ObjectId(questionId)}},
41
+ "$inc": {"historyCount": -1},
42
+ }
43
+
44
+ result = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS][
45
+ "chathistories"
46
+ ].update_one(
47
+ {"username": username, "history._id": ObjectId(questionId)}, update_query
48
+ )
49
+ print(result)
50
+ if result.matched_count != 0:
51
+ return ResponseHandler.success(4005)
52
+ else:
53
+ return ResponseHandler.error(4006, 404)
54
+ except Exception as error:
55
+ print("error:", error)
56
+ return ResponseHandler.error(9000, 500, error)
57
+
58
+
59
+ @router.get("/get")
60
+ async def get_chat_history_by_user_id(
61
+ page: Optional[int] = 1,
62
+ limit: Optional[int] = 10,
63
+ tokenData: str = Depends(verify_token),
64
+ ):
65
+ try:
66
+ username = tokenData["username"]
67
+
68
+ start_index = (page - 1) * limit
69
+
70
+ chat_history = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS][
71
+ "chathistories"
72
+ ].find_one(
73
+ {"username": username}, {"history": {"$slice": [start_index, limit]}}
74
+ )
75
+
76
+ if chat_history:
77
+ paginated_response = get_paginated_response(
78
+ chat_history["history"], page, limit, chat_history["historyCount"]
79
+ )
80
+ return ResponseHandler.success(4001, paginated_response)
81
+ else:
82
+ return ResponseHandler.error(4002, 404)
83
+ except Exception as error:
84
+ print("Error getting chat history:", error)
85
+ return ResponseHandler.error(9000)
src/helpers/json_convertor.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from bson import ObjectId
3
+ from datetime import datetime
4
+
5
+
6
+ def custom_serializer(obj):
7
+ if isinstance(obj, ObjectId):
8
+ return str(obj)
9
+ if isinstance(obj, datetime):
10
+ return obj.isoformat() # Convert datetime to ISO 8601 format
11
+ raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
12
+
13
+ def convert_to_json(data):
14
+ return json.dumps(data, default=custom_serializer, indent=4)
15
+
src/helpers/pagination.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+
3
+
4
+ def get_paginated_response(data, current_page, limit, total):
5
+ total_pages = math.ceil(total / limit)
6
+ current_page = int(current_page)
7
+ previous_page = current_page - 1 if current_page > 1 else None
8
+ next_page = current_page + 1 if current_page < total_pages else None
9
+
10
+ return {
11
+ "page_data": data,
12
+ "page_information": {
13
+ "total_data": total,
14
+ "last_page": total_pages,
15
+ "current_page": current_page,
16
+ "previous_page": previous_page,
17
+ "next_page": next_page,
18
+ },
19
+ }
src/helpers/response.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import Response
2
+ from src.langs.en.messages import get_message
3
+ from src.helpers.json_convertor import convert_to_json
4
+
5
+ class ResponseHandler:
6
+ @staticmethod
7
+ def success(message_code=None, data=None, status_code=200):
8
+ response = {
9
+ "success": True,
10
+ "message": get_message(message_code),
11
+ "data": data
12
+ }
13
+ return Response(content=convert_to_json(response), status_code=status_code, media_type="application/json")
14
+
15
+ @staticmethod
16
+ def error(message_code=9999, error=None, status_code=422, data=None):
17
+ response = {
18
+ "success": False,
19
+ "message": get_message(message_code),
20
+ "error": str(error) if error else None,
21
+ "data": data
22
+ }
23
+ return Response(content=convert_to_json(response), status_code=(500 if message_code == 9999 else status_code), media_type="application/json")
24
+
25
+ @staticmethod
26
+ def success_mediator(response):
27
+ return Response(content=(response.content), status_code=response.status_code, media_type="application/json")
src/helpers/send_request.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from requests import request
2
+
3
+
4
+ def sendRequest(url, method="get", data=None, files=None, headers=None):
5
+ try:
6
+ if files:
7
+ return request(method, url, data=data, files=files, headers=headers)
8
+ return request(method, url, json=data, headers=headers)
9
+ except Exception as e:
10
+ # Handle exceptions and return None
11
+ print(f"Error occurred during request: {e}")
12
+ return None
src/helpers/upload_file_cloudinary.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cloudinary
2
+ from cloudinary.uploader import upload
3
+ import io
4
+ from fastapi import UploadFile
5
+
6
+
7
+ # Define the upload_file function as an async function
8
+ async def upload_file(file: UploadFile, new_img_file_name: str, dir_address: str):
9
+ try:
10
+
11
+ # Upload the file to Cloudinary
12
+ result = upload(
13
+ file=file.file,
14
+ resource_type="auto",
15
+ public_id=new_img_file_name,
16
+ folder=dir_address,
17
+ )
18
+
19
+ return result
20
+ except Exception as error:
21
+ print(error)
22
+ raise error
src/langs/en/messages.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MESSAGES = {
2
+ # Bot messages
3
+ 2000: "Answered successfully.",
4
+ 2001: "Temporary chain created successfully.Will be deleted after 2 hours.",
5
+ 2002: "Embedding added successfully.",
6
+ 2003: "No files provided",
7
+ 2004: "Unsupported file format",
8
+ # Chat messages
9
+ 3000: "Chat created successfully.",
10
+ 3001: "Chats fetched successfully.",
11
+ 3002: "Chat fetched successfully.",
12
+ 3003: "Chat not found.",
13
+ 3004: "Chat updated successfully.",
14
+ 3005: "Chat deleted successfully.",
15
+ 3006: "Chat with this collection name already exists.",
16
+ 3007: "Icon file must be uploaded.",
17
+ # Chat history messages
18
+ 4000: "Chat history created successfully.",
19
+ 4001: "Chat history retrieved successfully.",
20
+ 4002: "Chat history not found.",
21
+ 4003: "Chat history updated successfully.",
22
+ 4004: "Chat history deleted successfully.",
23
+ 4005: "Question deleted successfully.",
24
+ 4006: "Question not found.",
25
+ # Authorization messages
26
+ 5001: "Unauthorized - Admin access required.",
27
+ 5002: "Access denied - Unauthorized.",
28
+ 5003: "Your session expired! Please log in again.",
29
+ # General messages
30
+ 9000: "Chatbot API server is running...",
31
+ 9999: "Internal Server Error.",
32
+ }
33
+
34
+
35
+ def get_message(message_code):
36
+ if isinstance(message_code, int) and message_code in MESSAGES:
37
+ return MESSAGES[message_code]
38
+
39
+ return message_code
src/main.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from src.routes.main import main_router
3
+ import src.config.cloudinaryConfig
4
+ from src.config.databaseConfig import DATABASE
5
+
6
+ origins = ["http://localhost:3000"]
7
+
8
+ main = APIRouter()
9
+
10
+ DATABASE()
11
+
12
+ main.include_router(main_router, prefix="/api", tags=["main"])
13
+
14
+ from src.helpers.response import ResponseHandler
15
+
16
+
17
+ @main.get("/")
18
+ async def home():
19
+ return ResponseHandler.success(message_code=9000)
src/middleware/verifyToken.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import HTTPException, Header, status
2
+ from src.config.appConfig import ENV_VAR, LOG
3
+ import jwt
4
+
5
+ async def verify_token(authorization: str = Header(None)):
6
+ try:
7
+ if not authorization or not authorization.startswith("Bearer "):
8
+ raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Token not provided or invalid")
9
+
10
+ token = authorization.split("Bearer ")[1]
11
+
12
+ try:
13
+ verified = jwt.decode(token, ENV_VAR.JWT_SECRET, algorithms=["HS256"])
14
+ LOG.debug("Token verified successfully")
15
+ except jwt.ExpiredSignatureError:
16
+ LOG.debug("Token expired")
17
+ raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Token expired")
18
+
19
+ return verified
20
+ except Exception as e:
21
+ LOG.error(f"An error occurred: {e}")
22
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
src/routes/auth_routes.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from src.controllers.auth.auth_controller import router as auth_routes
3
+ from src.controllers.auth.user_controller import router as user_routes
4
+ from src.controllers.auth.otp_controller import router as otp_routes
5
+
6
+ main_auth_router = APIRouter()
7
+
8
+ main_auth_router.include_router(auth_routes, prefix="/auth", tags=["Authentication"])
9
+ main_auth_router.include_router(user_routes, prefix="/user", tags=["Authentication"])
10
+ main_auth_router.include_router(otp_routes, prefix="/mail", tags=["Authentication"])
src/routes/chat_routes.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from src.controllers.chat.chats_controller import router as chat_routes
3
+ from src.controllers.chat.bot_controller import router as bot_routes
4
+ from src.controllers.chat.history_controller import router as history_routes
5
+
6
+ main_chat_router = APIRouter()
7
+
8
+ main_chat_router.include_router(chat_routes, prefix="", tags=["chatbot"])
9
+ main_chat_router.include_router(bot_routes, prefix="/bot", tags=["chatbot"])
10
+ main_chat_router.include_router(history_routes, prefix="/history", tags=["chatbot"])
src/routes/main.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from src.routes.auth_routes import main_auth_router
3
+ from src.routes.chat_routes import main_chat_router
4
+
5
+ main_router = APIRouter()
6
+
7
+ main_router.include_router(main_auth_router)
8
+ main_router.include_router(main_chat_router, prefix="/chat")
src/services/message_services.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from src.config.databaseConfig import DATABASE
2
+ from src.config.appConfig import ENV_VAR
3
+ from bson import ObjectId
4
+
5
+
6
+ async def save_question_and_answer_to_chat_history(username, history_obj):
7
+ try:
8
+ existing_chat_history = DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS][
9
+ "chathistories"
10
+ ].find_one({"username": username})
11
+ history_obj["_id"] = ObjectId()
12
+ if not existing_chat_history:
13
+ new_chat_history = {
14
+ "username": username,
15
+ "history": [history_obj],
16
+ "historyCount": 1,
17
+ }
18
+ DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS]["chathistories"].insert_one(
19
+ new_chat_history
20
+ )
21
+ else:
22
+ DATABASE.client[ENV_VAR.MONGO_DB_NAME_CHATS]["chathistories"].update_one(
23
+ {"username": username},
24
+ {
25
+ "$push": {"history": {"$each": [history_obj], "$position": 0}},
26
+ "$inc": {"historyCount": 1},
27
+ },
28
+ )
29
+ except Exception as error:
30
+ raise ValueError(f"Error saving question and answer to chat history: {error}")