Tanmay09516 commited on
Commit
9408cb5
·
verified ·
1 Parent(s): 4be32d4

backend for LangChat

Browse files
Files changed (5) hide show
  1. Dockerfile +18 -0
  2. app.py +91 -0
  3. nomic_embeddings.py +30 -0
  4. qdrant_search.py +40 -0
  5. requirements.txt +11 -0
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a base image
2
+ FROM python:3.9-slim
3
+
4
+ # Set the working directory
5
+ WORKDIR /app
6
+
7
+ # Copy requirements and install dependencies
8
+ COPY requirements.txt requirements.txt
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ # Copy the application code
12
+ COPY . .
13
+
14
+ # Expose the port FastAPI runs on
15
+ EXPOSE 8000
16
+
17
+ # Command to run the app
18
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
app.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ from dotenv import load_dotenv
4
+ from fastapi import FastAPI, HTTPException
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from pydantic import BaseModel
7
+ from qdrant_search import QdrantSearch
8
+ from langchain_groq import ChatGroq
9
+ from nomic_embeddings import EmbeddingsModel
10
+
11
+ load_dotenv()
12
+
13
+ import warnings
14
+ warnings.filterwarnings("ignore", category=FutureWarning)
15
+
16
+ os.environ["TOKENIZERS_PARALLELISM"] = "FALSE"
17
+
18
+ # Initialize FastAPI app
19
+ app = FastAPI()
20
+
21
+ # Allow CORS for frontend on Vercel
22
+ app.add_middleware(
23
+ CORSMiddleware,
24
+ allow_origins=["*"], # Replace "*" with your frontend URL for better security
25
+ allow_credentials=True,
26
+ allow_methods=["*"],
27
+ allow_headers=["*"],
28
+ )
29
+
30
+ # Initialize global variables
31
+ collection_names = ["docs_v1_2", "docs_v2_2", "docs_v3_2"]
32
+ limit = 5
33
+ llm = ChatGroq(model="mixtral-8x7b-32768")
34
+ embeddings = EmbeddingsModel()
35
+ search = QdrantSearch(
36
+ qdrant_url=os.environ["QDRANT_CLOUD_URL"],
37
+ api_key=os.environ["QDRANT_API_KEY"],
38
+ embeddings=embeddings
39
+ )
40
+
41
+ # Define request and response models
42
+ class QueryRequest(BaseModel):
43
+ question: str
44
+
45
+ class AnswerResponse(BaseModel):
46
+ answer: str
47
+ sources: list
48
+
49
+ # API endpoint to handle user queries
50
+ @app.post("/api/chat", response_model=AnswerResponse)
51
+ async def chat_endpoint(request: QueryRequest):
52
+ query = request.question.strip()
53
+ if not query:
54
+ raise HTTPException(status_code=400, detail="Query cannot be empty.")
55
+
56
+ # Step 1: Retrieve relevant documents from Qdrant
57
+ retrieved_docs = search.query_multiple_collections(query, collection_names, limit)
58
+
59
+ # Step 2: Prepare the context from retrieved documents
60
+ context = "\n".join([doc['text'] for doc in retrieved_docs])
61
+
62
+ # Step 3: Construct the prompt with context and question
63
+ prompt = (
64
+ "You are LangAssist, a knowledgeable assistant for the LangChain Python Library. "
65
+ "Given the following context from the documentation, provide a helpful answer to the user's question.\n\n"
66
+ "Context:\n{context}\n\n"
67
+ "Question: {question}\n\n"
68
+ "Answer:"
69
+ ).format(context=context, question=query)
70
+
71
+ # Step 4: Generate an answer using the language model
72
+ try:
73
+ answer = llm.invoke(prompt)
74
+ except Exception as e:
75
+ raise HTTPException(status_code=500, detail=str(e))
76
+
77
+ # Prepare sources
78
+ sources = [
79
+ {
80
+ "source": doc['source'],
81
+ "text": doc['text']
82
+ } for doc in retrieved_docs
83
+ ]
84
+
85
+ # Step 5: Return the answer and sources
86
+ # return AnswerResponse(answer=answer.strip(), sources=sources)
87
+ return AnswerResponse(answer=answer.content.strip(), sources=sources)
88
+
89
+ if __name__ == "__main__":
90
+ import uvicorn
91
+ uvicorn.run(app, host="0.0.0.0", port=8000)
nomic_embeddings.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_community.embeddings import HuggingFaceBgeEmbeddings
2
+
3
+ class EmbeddingsModel:
4
+ def __init__(self):
5
+ model_name = "nomic-ai/nomic-embed-text-v1"
6
+ model_kwargs = {
7
+ 'device': 'cpu',
8
+ 'trust_remote_code': True
9
+ }
10
+ encode_kwargs = {'normalize_embeddings': True}
11
+ self.embeddings = HuggingFaceBgeEmbeddings(
12
+ model_name=model_name,
13
+ model_kwargs=model_kwargs,
14
+ encode_kwargs=encode_kwargs,
15
+ query_instruction="search_query:",
16
+ embed_instruction="search_document:"
17
+ )
18
+
19
+ def get_embeddings(self, text):
20
+ """
21
+ Returns the embeddings for the given text.
22
+
23
+ :param text: The input text to get embeddings for.
24
+ :return: The embeddings as a numpy array.
25
+ """
26
+ return self.embeddings.embed_query(text)
27
+
28
+ # Example usage:
29
+ # embeddings_model = EmbeddingsModel()
30
+ # embeddings = embeddings_model.get_embeddings("Your input text here")
qdrant_search.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # qdrant_search.py
2
+
3
+ from typing import List, Dict
4
+ from langchain_community.embeddings import HuggingFaceBgeEmbeddings
5
+ from qdrant_client import QdrantClient
6
+
7
+ class QdrantSearch:
8
+ def __init__(self, qdrant_url: str, api_key: str, embeddings):
9
+ self.embeddings=embeddings
10
+ # Initialize Qdrant client
11
+ self.client = QdrantClient(
12
+ url=qdrant_url,
13
+ api_key=api_key,
14
+ )
15
+
16
+ def query_qdrant(self, query: str, collection_name: str, limit: int = 5) -> List:
17
+ """Retrieve relevant documents from Qdrant."""
18
+ query_vector = self.embeddings.get_embeddings(query)
19
+
20
+ results = self.client.search(
21
+ collection_name=collection_name,
22
+ query_vector=query_vector,
23
+ limit=limit
24
+ )
25
+ return results
26
+
27
+ def query_multiple_collections(self, query: str, collection_names: List[str], limit: int = 5) -> List[Dict]:
28
+ """Query multiple Qdrant collections and return combined top results."""
29
+ all_results = []
30
+
31
+ for collection_name in collection_names:
32
+ results = self.query_qdrant(query, collection_name, limit)
33
+ for result in results:
34
+ all_results.append({
35
+ 'text': result.payload['text'],
36
+ 'source': result.payload['source'],
37
+ 'score': result.score
38
+ })
39
+
40
+ return sorted(all_results, key=lambda x: x['score'], reverse=True)[:limit]
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ python-dotenv
4
+ qdrant-client
5
+ langchain
6
+ langchain_community
7
+ langchain_groq
8
+ pydantic
9
+ fastapi
10
+ einops
11
+ sentence-transformers