Saran08 commited on
Commit
6ad7021
·
1 Parent(s): aff3cb9

made several changes in all scripts

Browse files
Files changed (5) hide show
  1. Dockerfile +0 -16
  2. main.py +8 -21
  3. rag_chatbot.py +3 -16
  4. rank.py +1 -54
  5. recommender.py +2 -12
Dockerfile CHANGED
@@ -1,36 +1,20 @@
1
- # Use an official Python runtime as a parent image
2
  FROM python:3.10-slim
3
 
4
- # Set the working directory in the container
5
  WORKDIR /app
6
 
7
- # --- THIS IS THE CRITICAL NEW SECTION ---
8
- # Install system dependencies required for building llama-cpp-python
9
- # - apt-get update: Refreshes the package list
10
- # - build-essential: Installs C/C++ compilers (gcc, g++) and make
11
- # - cmake: The build system generator used by llama-cpp-python
12
- # - --no-install-recommends: Reduces image size by not installing optional packages
13
- # - rm -rf /var/lib/apt/lists/*: Cleans up the apt cache to keep the image small
14
  RUN apt-get update && apt-get install -y --no-install-recommends \
15
  build-essential \
16
  cmake \
17
  && rm -rf /var/lib/apt/lists/*
18
- # --- END OF NEW SECTION ---
19
 
20
- # Copy the requirements file into the container at /app
21
  COPY requirements.txt .
22
 
23
- # Install any needed packages specified in requirements.txt
24
  RUN pip install --no-cache-dir -r requirements.txt
25
 
26
- # Copy the local 'data' directory into the container's /app/data directory
27
  COPY ./data ./data
28
 
29
- # Copy the rest of the application source code
30
  COPY . .
31
 
32
- # Expose the port the app runs on
33
  EXPOSE 8000
34
 
35
- # Command to run the application
36
  CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
1
  FROM python:3.10-slim
2
 
 
3
  WORKDIR /app
4
 
 
 
 
 
 
 
 
5
  RUN apt-get update && apt-get install -y --no-install-recommends \
6
  build-essential \
7
  cmake \
8
  && rm -rf /var/lib/apt/lists/*
 
9
 
 
10
  COPY requirements.txt .
11
 
 
12
  RUN pip install --no-cache-dir -r requirements.txt
13
 
 
14
  COPY ./data ./data
15
 
 
16
  COPY . .
17
 
 
18
  EXPOSE 8000
19
 
 
20
  CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py CHANGED
@@ -1,10 +1,9 @@
1
- # main.py
2
  from fastapi import FastAPI, HTTPException
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
5
  from typing import List
6
  import uvicorn
7
- import traceback # <-- IMPORT THE TRACEBACK MODULE
8
  from deep_translator import GoogleTranslator
9
  from rag_chatbot import RAGChatBot
10
  from recommender import QuestionRecommender
@@ -63,39 +62,33 @@ async def startup_event():
63
  """
64
  global bot, recommender
65
  try:
66
- # 1. Create the embedding model - THE SINGLE SOURCE OF TRUTH
67
  print(f"Loading embedding model: {EMBEDDING_MODEL_NAME}")
68
  embeddings = HuggingFaceEmbeddings(
69
  model_name=EMBEDDING_MODEL_NAME,
70
  model_kwargs={'device': 'cpu'}
71
  )
72
- print("Embedding model loaded.")
73
 
74
- # 2. Initialize the RAG ChatBot (pass embeddings)
75
  print("Loading RAG ChatBot...")
76
- bot = RAGChatBot(embeddings) # ✅ FIXED
77
 
78
- faiss_path = os.path.join("data", "faiss.index") # Corrected name to match your file
79
  questions_path = os.path.join("data", "questions.npy")
80
 
81
- print(f"Attempting to load Recommender index from: '{faiss_path}'") # Add for debugging
82
 
83
- # Initialize the Question Recommender with the corrected paths
84
  recommender = QuestionRecommender(
85
  faiss_index_path=faiss_path,
86
  questions_path=questions_path,
87
  embedding_model=embeddings
88
  )
89
- print("🎉 All models loaded successfully! API is ready. 🎉")
90
 
91
  except Exception as e:
92
- print("💥 Critical Error: Failed to load models during startup:")
93
  traceback.print_exc()
94
  raise e
95
 
96
-
97
- # --- Helper Functions ---
98
-
99
  def translate_text(text: str, target_lang: str, source_lang: str = "auto") -> str:
100
  if not text or source_lang == target_lang or (target_lang == "en" and source_lang == "auto"):
101
  return text
@@ -119,8 +112,6 @@ def get_bot_response(query_en: str) -> tuple[str, List[str]]:
119
 
120
  return answer_en, new_recommendations
121
 
122
- # --- API Endpoints ---
123
-
124
  @app.get("/")
125
  async def root():
126
  return {"message": "Sat2Farm AI Assistant API is running!"}
@@ -155,9 +146,7 @@ async def get_initial_recommendations():
155
  async def chat(request: ChatRequest):
156
  """Main endpoint to process a user's chat message."""
157
  if not bot or not recommender:
158
- raise HTTPException(status_code=503, detail="Chat service is not available. Models are not loaded.")
159
-
160
- # ========================== THE FIX IS HERE ==========================
161
  try:
162
  query_en = translate_text(request.message, target_lang="en", source_lang=request.user_language)
163
 
@@ -170,11 +159,9 @@ async def chat(request: ChatRequest):
170
  recommendations=new_recommendations_en
171
  )
172
  except Exception as e:
173
- # This will now print the FULL error traceback to your logs
174
  print(f"Error during /chat processing. Full traceback below:")
175
  traceback.print_exc()
176
  raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
177
- # =====================================================================
178
 
179
  @app.post("/recommendations/action")
180
  async def handle_recommendation_action(request: RecommendationRequest):
 
 
1
  from fastapi import FastAPI, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from pydantic import BaseModel
4
  from typing import List
5
  import uvicorn
6
+ import traceback
7
  from deep_translator import GoogleTranslator
8
  from rag_chatbot import RAGChatBot
9
  from recommender import QuestionRecommender
 
62
  """
63
  global bot, recommender
64
  try:
 
65
  print(f"Loading embedding model: {EMBEDDING_MODEL_NAME}")
66
  embeddings = HuggingFaceEmbeddings(
67
  model_name=EMBEDDING_MODEL_NAME,
68
  model_kwargs={'device': 'cpu'}
69
  )
70
+ print("Embedding model loaded.")
71
 
 
72
  print("Loading RAG ChatBot...")
73
+ bot = RAGChatBot(embeddings)
74
 
75
+ faiss_path = os.path.join("data", "faiss.index")
76
  questions_path = os.path.join("data", "questions.npy")
77
 
78
+ print(f"Attempting to load Recommender index from: '{faiss_path}'")
79
 
 
80
  recommender = QuestionRecommender(
81
  faiss_index_path=faiss_path,
82
  questions_path=questions_path,
83
  embedding_model=embeddings
84
  )
85
+ print("All models loaded successfully! API is ready.")
86
 
87
  except Exception as e:
88
+ print("Critical Error: Failed to load models during startup:")
89
  traceback.print_exc()
90
  raise e
91
 
 
 
 
92
  def translate_text(text: str, target_lang: str, source_lang: str = "auto") -> str:
93
  if not text or source_lang == target_lang or (target_lang == "en" and source_lang == "auto"):
94
  return text
 
112
 
113
  return answer_en, new_recommendations
114
 
 
 
115
  @app.get("/")
116
  async def root():
117
  return {"message": "Sat2Farm AI Assistant API is running!"}
 
146
  async def chat(request: ChatRequest):
147
  """Main endpoint to process a user's chat message."""
148
  if not bot or not recommender:
149
+ raise HTTPException(status_code=503, detail="Chat service is not available. Models are not loaded.")
 
 
150
  try:
151
  query_en = translate_text(request.message, target_lang="en", source_lang=request.user_language)
152
 
 
159
  recommendations=new_recommendations_en
160
  )
161
  except Exception as e:
 
162
  print(f"Error during /chat processing. Full traceback below:")
163
  traceback.print_exc()
164
  raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
 
165
 
166
  @app.post("/recommendations/action")
167
  async def handle_recommendation_action(request: RecommendationRequest):
rag_chatbot.py CHANGED
@@ -1,10 +1,7 @@
1
- # rag_chatbot.py
2
-
3
  import os
4
  from huggingface_hub import hf_hub_download
5
  from langchain_community.document_loaders import PyPDFLoader
6
  from langchain_community.vectorstores import FAISS
7
- # <<< CHANGED: Import from the new, correct package
8
  from langchain_huggingface import HuggingFaceEmbeddings
9
  from langchain_community.retrievers import BM25Retriever
10
  from langchain.text_splitter import RecursiveCharacterTextSplitter
@@ -16,13 +13,8 @@ from langchain.retrievers.document_compressors import FlashrankRerank
16
  from flashrank import Ranker
17
  from typing import Dict, Any, List
18
 
19
- # This is a one-time operation for flashrank, it's fine to keep it here.
20
- # FlashrankRerank.model_rebuild()
21
-
22
- # --- Configuration ---
23
  PDF_PATH = "data/sat2farm_doc.pdf"
24
- # <<< REMOVED: This constant is no longer needed here as the model is passed in.
25
- # EMBEDDING_MODEL = "BAAI/bge-large-en-v1.5"
26
  MODEL_NAME = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF"
27
  MODEL_FILE = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
28
 
@@ -37,7 +29,6 @@ class RAGChatBot:
37
  print(f"Downloading GGUF model: {MODEL_NAME}/{MODEL_FILE}")
38
  model_path = hf_hub_download(repo_id=MODEL_NAME, filename=MODEL_FILE)
39
 
40
- # Use the provided embedding model to build the chain.
41
  self.chain = self._create_rag_chain(model_path, embeddings)
42
  print("\n✅ RAG ChatBot initialized successfully!")
43
 
@@ -54,12 +45,10 @@ class RAGChatBot:
54
  chunks = text_splitter.split_documents(documents)
55
  print(f"Created {len(chunks)} document chunks.")
56
 
57
- # STAGE 1: RECALL (Ensemble Retriever)
58
  print("Initializing Stage 1 Retriever (Ensemble)...")
59
  bm25_retriever = BM25Retriever.from_documents(chunks)
60
  bm25_retriever.k = 10
61
 
62
- # This line is key: it uses the centrally-managed `embeddings` object.
63
  vectorstore = FAISS.from_documents(chunks, embeddings)
64
  faiss_retriever = vectorstore.as_retriever(search_kwargs={'k': 10})
65
 
@@ -67,7 +56,6 @@ class RAGChatBot:
67
  retrievers=[bm25_retriever, faiss_retriever], weights=[0.5, 0.5], search_type="rrf"
68
  )
69
 
70
- # STAGE 2: RE-RANK (Flashrank)
71
  print("Initializing Stage 2 Re-ranker (Flashrank)...")
72
  compressor = FlashrankRerank(top_n=3)
73
  final_retriever = ContextualCompressionRetriever(
@@ -76,7 +64,7 @@ class RAGChatBot:
76
 
77
  print(f"Initializing GGUF LLM from: {model_path}")
78
  llm = LlamaCpp(
79
- model_path=model_path, n_ctx=2048, n_gpu_layers=-1, # Use -1 for all layers on GPU
80
  temperature=0.0, top_k=1, verbose=False, max_tokens=512
81
  )
82
 
@@ -85,6 +73,7 @@ You are a factual question-answering assistant. Your task is to answer the user'
85
  Follow these rules strictly:
86
  1. Provide only the direct answer to the question and nothing else. DO NOT add any summary, conclusion, or other extra information.
87
  2. If the answer is not in the context, state that you do not have that information.
 
88
 
89
  Context: {context}
90
  Question: {question}
@@ -108,12 +97,10 @@ Helpful Answer:"""
108
  print(f"Invoking RAG chain with query: '{query}'")
109
  result = self.chain.invoke(query)
110
 
111
- # Clean up the answer
112
  answer = result.get("result", "").strip()
113
  if "Helpful Answer:" in answer:
114
  answer = answer.split("Helpful Answer:")[1].strip()
115
 
116
- # Format sources
117
  sources = []
118
  if result.get("source_documents"):
119
  for doc in result["source_documents"]:
 
 
 
1
  import os
2
  from huggingface_hub import hf_hub_download
3
  from langchain_community.document_loaders import PyPDFLoader
4
  from langchain_community.vectorstores import FAISS
 
5
  from langchain_huggingface import HuggingFaceEmbeddings
6
  from langchain_community.retrievers import BM25Retriever
7
  from langchain.text_splitter import RecursiveCharacterTextSplitter
 
13
  from flashrank import Ranker
14
  from typing import Dict, Any, List
15
 
 
 
 
 
16
  PDF_PATH = "data/sat2farm_doc.pdf"
17
+
 
18
  MODEL_NAME = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF"
19
  MODEL_FILE = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
20
 
 
29
  print(f"Downloading GGUF model: {MODEL_NAME}/{MODEL_FILE}")
30
  model_path = hf_hub_download(repo_id=MODEL_NAME, filename=MODEL_FILE)
31
 
 
32
  self.chain = self._create_rag_chain(model_path, embeddings)
33
  print("\n✅ RAG ChatBot initialized successfully!")
34
 
 
45
  chunks = text_splitter.split_documents(documents)
46
  print(f"Created {len(chunks)} document chunks.")
47
 
 
48
  print("Initializing Stage 1 Retriever (Ensemble)...")
49
  bm25_retriever = BM25Retriever.from_documents(chunks)
50
  bm25_retriever.k = 10
51
 
 
52
  vectorstore = FAISS.from_documents(chunks, embeddings)
53
  faiss_retriever = vectorstore.as_retriever(search_kwargs={'k': 10})
54
 
 
56
  retrievers=[bm25_retriever, faiss_retriever], weights=[0.5, 0.5], search_type="rrf"
57
  )
58
 
 
59
  print("Initializing Stage 2 Re-ranker (Flashrank)...")
60
  compressor = FlashrankRerank(top_n=3)
61
  final_retriever = ContextualCompressionRetriever(
 
64
 
65
  print(f"Initializing GGUF LLM from: {model_path}")
66
  llm = LlamaCpp(
67
+ model_path=model_path, n_ctx=2048, n_gpu_layers=-1,
68
  temperature=0.0, top_k=1, verbose=False, max_tokens=512
69
  )
70
 
 
73
  Follow these rules strictly:
74
  1. Provide only the direct answer to the question and nothing else. DO NOT add any summary, conclusion, or other extra information.
75
  2. If the answer is not in the context, state that you do not have that information.
76
+ 3. The CEO of Satyukt Analytics is Dr. Satkumar Tomar, if user asks question about who is ceo, tell this answer.
77
 
78
  Context: {context}
79
  Question: {question}
 
97
  print(f"Invoking RAG chain with query: '{query}'")
98
  result = self.chain.invoke(query)
99
 
 
100
  answer = result.get("result", "").strip()
101
  if "Helpful Answer:" in answer:
102
  answer = answer.split("Helpful Answer:")[1].strip()
103
 
 
104
  sources = []
105
  if result.get("source_documents"):
106
  for doc in result["source_documents"]:
rank.py CHANGED
@@ -1,67 +1,19 @@
1
-
2
  import numpy as np
3
  import faiss
4
  from sentence_transformers import SentenceTransformer
5
  import importlib, subprocess, sys
6
  from rank_bm25 import BM25Okapi
7
- # class HybridChatBot:
8
- # def __init__(self, model_name="all-MiniLM-L6-v2", index_file="Chatbot/data/faiss.index"):
9
- # # Load embeddings index
10
- # self.model = SentenceTransformer(model_name)
11
- # self.index = faiss.read_index(index_file)
12
- # self.questions = np.load("Chatbot/data/questions.npy", allow_pickle=True)
13
- # self.answers = np.load("Chatbot/data/answers.npy", allow_pickle=True)
14
-
15
- # # Prepare BM25
16
- # tokenized_corpus = [q.lower().split() for q in self.questions]
17
- # self.bm25 = BM25Okapi(tokenized_corpus)
18
-
19
- # def search(self, query, top_k, alpha):
20
- # """
21
- # Hybrid search:
22
- # alpha = weight for BM25 vs embeddings (0.5 = equal weight)
23
- # """
24
- # # --- Embedding Search ---
25
- # query_embedding = self.model.encode([query], convert_to_numpy=True)
26
- # distances, indices = self.index.search(query_embedding, top_k)
27
- # embedding_scores = {idx: 1/(1+dist) for idx, dist in zip(indices[0], distances[0])}
28
-
29
- # # --- BM25 Search ---
30
- # bm25_scores = self.bm25.get_scores(query.lower().split())
31
- # bm25_top = np.argsort(bm25_scores)[::-1][:top_k]
32
- # bm25_scores = {idx: bm25_scores[idx] for idx in bm25_top}
33
-
34
- # # --- Combine Scores ---
35
- # combined_scores = {}
36
- # for idx in set(list(embedding_scores.keys()) + list(bm25_scores.keys())):
37
- # emb_score = embedding_scores.get(idx, 0)
38
- # bm_score = bm25_scores.get(idx, 0)
39
- # combined_scores[idx] = alpha * bm_score + (1 - alpha) * emb_score
40
-
41
- # # --- Sort and Return ---
42
- # best = sorted(combined_scores.items(), key=lambda x: x[1], reverse=True)
43
 
44
- # results = []
45
- # for idx, score in best[:top_k]:
46
- # results.append({
47
- # "matched_question": self.questions[idx],
48
- # "answer": self.answers[idx],
49
- # "score": float(score)
50
- # })
51
- # return results
52
  class HybridChatBot:
53
  def __init__(self, model_name="all-MiniLM-L6-v2", index_file="data/faiss.index", fallback_threshold=0.05):
54
- # Load embeddings index
55
  self.model = SentenceTransformer(model_name)
56
  self.index = faiss.read_index(index_file)
57
  self.questions = np.load("data/questions.npy", allow_pickle=True)
58
  self.answers = np.load("data/answers.npy", allow_pickle=True)
59
 
60
- # Prepare BM25
61
  tokenized_corpus = [q.lower().split() for q in self.questions]
62
  self.bm25 = BM25Okapi(tokenized_corpus)
63
 
64
- # Threshold for fallback
65
  self.fallback_threshold = fallback_threshold
66
 
67
  def search(self, query, top_k=5, alpha=0.5):
@@ -69,29 +21,24 @@ class HybridChatBot:
69
  Hybrid search:
70
  alpha = weight for BM25 vs embeddings (0.5 = equal weight)
71
  """
72
- # --- Embedding Search ---
73
  query_embedding = self.model.encode([query], convert_to_numpy=True)
74
  distances, indices = self.index.search(query_embedding, top_k)
75
  embedding_scores = {idx: 1/(1+dist) for idx, dist in zip(indices[0], distances[0])}
76
 
77
- # --- BM25 Search ---
78
  bm25_scores = self.bm25.get_scores(query.lower().split())
79
  bm25_top = np.argsort(bm25_scores)[::-1][:top_k]
80
  bm25_scores = {idx: bm25_scores[idx] for idx in bm25_top}
81
 
82
- # --- Combine Scores ---
83
  combined_scores = {}
84
  for idx in set(list(embedding_scores.keys()) + list(bm25_scores.keys())):
85
  emb_score = embedding_scores.get(idx, 0)
86
  bm_score = bm25_scores.get(idx, 0)
87
  combined_scores[idx] = alpha * bm_score + (1 - alpha) * emb_score
88
 
89
- # --- Sort and Return ---
90
  best = sorted(combined_scores.items(), key=lambda x: x[1], reverse=True)
91
 
92
  results = []
93
  if not best or best[0][1] < self.fallback_threshold:
94
- # Low confidence → fallback message
95
  results.append({
96
  "matched_question": None,
97
  "answer": "Sorry, I couldn't find a reliable answer. Please contact our support team.",
@@ -105,4 +52,4 @@ class HybridChatBot:
105
  "score": float(score)
106
  })
107
 
108
- return results
 
 
1
  import numpy as np
2
  import faiss
3
  from sentence_transformers import SentenceTransformer
4
  import importlib, subprocess, sys
5
  from rank_bm25 import BM25Okapi
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
 
 
 
 
 
 
 
 
7
  class HybridChatBot:
8
  def __init__(self, model_name="all-MiniLM-L6-v2", index_file="data/faiss.index", fallback_threshold=0.05):
 
9
  self.model = SentenceTransformer(model_name)
10
  self.index = faiss.read_index(index_file)
11
  self.questions = np.load("data/questions.npy", allow_pickle=True)
12
  self.answers = np.load("data/answers.npy", allow_pickle=True)
13
 
 
14
  tokenized_corpus = [q.lower().split() for q in self.questions]
15
  self.bm25 = BM25Okapi(tokenized_corpus)
16
 
 
17
  self.fallback_threshold = fallback_threshold
18
 
19
  def search(self, query, top_k=5, alpha=0.5):
 
21
  Hybrid search:
22
  alpha = weight for BM25 vs embeddings (0.5 = equal weight)
23
  """
 
24
  query_embedding = self.model.encode([query], convert_to_numpy=True)
25
  distances, indices = self.index.search(query_embedding, top_k)
26
  embedding_scores = {idx: 1/(1+dist) for idx, dist in zip(indices[0], distances[0])}
27
 
 
28
  bm25_scores = self.bm25.get_scores(query.lower().split())
29
  bm25_top = np.argsort(bm25_scores)[::-1][:top_k]
30
  bm25_scores = {idx: bm25_scores[idx] for idx in bm25_top}
31
 
 
32
  combined_scores = {}
33
  for idx in set(list(embedding_scores.keys()) + list(bm25_scores.keys())):
34
  emb_score = embedding_scores.get(idx, 0)
35
  bm_score = bm25_scores.get(idx, 0)
36
  combined_scores[idx] = alpha * bm_score + (1 - alpha) * emb_score
37
 
 
38
  best = sorted(combined_scores.items(), key=lambda x: x[1], reverse=True)
39
 
40
  results = []
41
  if not best or best[0][1] < self.fallback_threshold:
 
42
  results.append({
43
  "matched_question": None,
44
  "answer": "Sorry, I couldn't find a reliable answer. Please contact our support team.",
 
52
  "score": float(score)
53
  })
54
 
55
+ return results
recommender.py CHANGED
@@ -1,8 +1,6 @@
1
- # recommend.py (Upgraded Version)
2
-
3
  import faiss
4
  import numpy as np
5
- from langchain_community.embeddings import HuggingFaceEmbeddings # Import this
6
 
7
  class QuestionRecommender:
8
  def __init__(self, faiss_index_path, questions_path, embedding_model: HuggingFaceEmbeddings, top_k=5):
@@ -12,7 +10,7 @@ class QuestionRecommender:
12
  print("Initializing Question Recommender...")
13
  self.index = faiss.read_index(faiss_index_path)
14
  self.questions = np.load(questions_path, allow_pickle=True)
15
- self.embedding_model = embedding_model # <-- NEW: Store the embedding model
16
  self.top_k = top_k
17
  self.start_questions = [
18
  "What is Sat2Farm?",
@@ -39,23 +37,17 @@ class QuestionRecommender:
39
  self.history.append(self.current_recommendations)
40
 
41
  embedding = None
42
- # First, try the fast path: see if the query is a known question
43
  try:
44
  q_idx = np.where(self.questions == query)[0][0]
45
- # If found, reconstruct its embedding directly from the index (very fast)
46
  embedding = self.index.reconstruct(int(q_idx)).reshape(1, -1)
47
  print(f"Recommending based on known question: '{query}'")
48
  except IndexError:
49
- # This is the new, powerful part!
50
- # If the query is not in our list, embed it on the fly.
51
  print(f"Recommending based on new user query: '{query}'")
52
  embedding = np.array(self.embedding_model.embed_query(query)).reshape(1, -1)
53
 
54
  if embedding is not None:
55
- # Search for similar question embeddings in our FAISS index
56
  distances, indices = self.index.search(embedding, self.top_k + 1)
57
 
58
- # Create the list of recommended questions from the search results
59
  recommended = [
60
  self.questions[i] for i in indices[0]
61
  if i < len(self.questions) and self.questions[i] != query
@@ -64,10 +56,8 @@ class QuestionRecommender:
64
  self.current_recommendations = recommended[:self.top_k]
65
  return self.current_recommendations
66
 
67
- # Fallback if something went wrong
68
  return self.start_questions
69
 
70
-
71
  def go_back(self):
72
  """Returns the previous set of recommended questions from history."""
73
  if self.history:
 
 
 
1
  import faiss
2
  import numpy as np
3
+ from langchain_community.embeddings import HuggingFaceEmbeddings
4
 
5
  class QuestionRecommender:
6
  def __init__(self, faiss_index_path, questions_path, embedding_model: HuggingFaceEmbeddings, top_k=5):
 
10
  print("Initializing Question Recommender...")
11
  self.index = faiss.read_index(faiss_index_path)
12
  self.questions = np.load(questions_path, allow_pickle=True)
13
+ self.embedding_model = embedding_model
14
  self.top_k = top_k
15
  self.start_questions = [
16
  "What is Sat2Farm?",
 
37
  self.history.append(self.current_recommendations)
38
 
39
  embedding = None
 
40
  try:
41
  q_idx = np.where(self.questions == query)[0][0]
 
42
  embedding = self.index.reconstruct(int(q_idx)).reshape(1, -1)
43
  print(f"Recommending based on known question: '{query}'")
44
  except IndexError:
 
 
45
  print(f"Recommending based on new user query: '{query}'")
46
  embedding = np.array(self.embedding_model.embed_query(query)).reshape(1, -1)
47
 
48
  if embedding is not None:
 
49
  distances, indices = self.index.search(embedding, self.top_k + 1)
50
 
 
51
  recommended = [
52
  self.questions[i] for i in indices[0]
53
  if i < len(self.questions) and self.questions[i] != query
 
56
  self.current_recommendations = recommended[:self.top_k]
57
  return self.current_recommendations
58
 
 
59
  return self.start_questions
60
 
 
61
  def go_back(self):
62
  """Returns the previous set of recommended questions from history."""
63
  if self.history: