harivarshannn commited on
Commit
1fb1192
·
1 Parent(s): 2be24d4

Add LangChain RAG pipeline for custom PDF advisory with LFS tracking

Browse files
Files changed (8) hide show
  1. .gitattributes +1 -0
  2. 1_agro.pdf +3 -0
  3. 2-agro.pdf +3 -0
  4. 3-agrogpt.pdf +3 -0
  5. 4-agrogpt.pdf +3 -0
  6. app.py +14 -3
  7. rag_engine.py +103 -0
  8. requirements.txt +30 -0
.gitattributes CHANGED
@@ -1,2 +1,3 @@
1
  *.pt filter=lfs diff=lfs merge=lfs -text
2
  *.png filter=lfs diff=lfs merge=lfs -text
 
 
1
  *.pt filter=lfs diff=lfs merge=lfs -text
2
  *.png filter=lfs diff=lfs merge=lfs -text
3
+ *.pdf filter=lfs diff=lfs merge=lfs -text
1_agro.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fe196417aa478f1807235c513e6e9c4f23a3c52ecc7fcc4dc42703b7de53b2a0
3
+ size 8617145
2-agro.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e674cd3b6a214696d24d6876892cee6ffab3ae7e9499017f573c25690e1b901e
3
+ size 17831258
3-agrogpt.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:adbb66a8efc7bc24cb6b4afc99782519cf41d8b1ca0414c486db6627d7bc5c7f
3
+ size 4316300
4-agrogpt.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2e5c45b5cefa9eb34f7abba0e72e91ea20feba58e67bfbd0f0706f980d0df48d
3
+ size 6330107
app.py CHANGED
@@ -18,6 +18,7 @@ import requests
18
  # Import the new model functionality
19
  from model import check_ollama_connection, generate_with_ollama, analyze_image_for_disease
20
  from weather import get_current_weather, TN_DISTRICTS
 
21
 
22
  app = Flask(__name__)
23
  app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 # Disable static file caching
@@ -114,6 +115,10 @@ def check_backend_status():
114
  status_message = "Error: Could not connect to Ollama. Ensure GROQ_API_KEY is set in .env."
115
  print(status_message, flush=True)
116
 
 
 
 
 
117
  @app.route('/')
118
  def index():
119
  """Serve the main page"""
@@ -219,15 +224,21 @@ def ask_question():
219
  try:
220
  # Construct prompt
221
  weather_section = f"\n\n[Weather Context]: {weather_context.strip()}" if weather_context.strip() else ""
 
 
 
 
 
 
222
  full_prompt = (
223
- "You are AgroGPT, an expert agriculture assistant. "
224
  "Answer the following question clearly and concisely in plain text. "
225
  "Structure your response EXACTLY as follows:\n"
226
- "1. Provide a helpful answer in English.\n"
227
  "2. Then write the header 'Malayalam Summary:' followed by the FULL answer translated into native Malayalam script (മലയാളം). Do NOT use English/Latin letters for Malayalam.\n"
228
  "3. Then write the header 'Tamil Summary:' followed by the FULL answer translated into native Tamil script (தமிழ்). Do NOT use English/Latin letters for Tamil.\n"
229
  "Use double line breaks between each section. Do NOT use markdown, asterisks, or bullet points.\n\n"
230
- f"Question: {question}{weather_section}\n\nAnswer:"
231
  )
232
 
233
  print(f"Asking Ollama: {question}", flush=True)
 
18
  # Import the new model functionality
19
  from model import check_ollama_connection, generate_with_ollama, analyze_image_for_disease
20
  from weather import get_current_weather, TN_DISTRICTS
21
+ from rag_engine import initialize_knowledge_base, query_rag
22
 
23
  app = Flask(__name__)
24
  app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 # Disable static file caching
 
115
  status_message = "Error: Could not connect to Ollama. Ensure GROQ_API_KEY is set in .env."
116
  print(status_message, flush=True)
117
 
118
+ # Initialize the vector DB in the background
119
+ print("Triggering background RAG knowledge base initialization...", flush=True)
120
+ threading.Thread(target=initialize_knowledge_base).start()
121
+
122
  @app.route('/')
123
  def index():
124
  """Serve the main page"""
 
224
  try:
225
  # Construct prompt
226
  weather_section = f"\n\n[Weather Context]: {weather_context.strip()}" if weather_context.strip() else ""
227
+
228
+ # Fetch RAG context
229
+ print("Fetching expert PDF knowledge context...", flush=True)
230
+ rag_context = query_rag(question)
231
+ rag_section = f"\n\n[Expert PDF Advisory Knowledge Base Context]:\n{rag_context}\n\nUse this information above to enhance your answer if relevant." if rag_context else ""
232
+
233
  full_prompt = (
234
+ "You are AgroGPT, an expert agriculture assistant with access to detailed agricultural advisory documents. "
235
  "Answer the following question clearly and concisely in plain text. "
236
  "Structure your response EXACTLY as follows:\n"
237
+ "1. Provide a helpful, robust answer in English. Ensure you include relevant information from the knowledge base if available.\n"
238
  "2. Then write the header 'Malayalam Summary:' followed by the FULL answer translated into native Malayalam script (മലയാളം). Do NOT use English/Latin letters for Malayalam.\n"
239
  "3. Then write the header 'Tamil Summary:' followed by the FULL answer translated into native Tamil script (தமிழ்). Do NOT use English/Latin letters for Tamil.\n"
240
  "Use double line breaks between each section. Do NOT use markdown, asterisks, or bullet points.\n\n"
241
+ f"Question: {question}{weather_section}{rag_section}\n\nAnswer:"
242
  )
243
 
244
  print(f"Asking Ollama: {question}", flush=True)
rag_engine.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ from langchain_community.document_loaders import PyPDFLoader
4
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
5
+ from langchain_huggingface import HuggingFaceEmbeddings
6
+ from langchain_community.vectorstores import FAISS
7
+
8
+ # Path to local PDFs
9
+ PDF_FILES = [
10
+ "1_agro.pdf",
11
+ "2-agro.pdf",
12
+ "3-agrogpt.pdf",
13
+ "4-agrogpt.pdf"
14
+ ]
15
+
16
+ # Writeable path on Hugging Face for the vector store
17
+ VECTORSTORE_DIR = "/tmp/vectorstore"
18
+
19
+ # Global variable to hold the FAISS index in memory
20
+ _vector_store = None
21
+
22
+ def get_embeddings_model():
23
+ """Return the HuggingFace embeddings model. Uses a lightweight fast model."""
24
+ # Using all-MiniLM-L6-v2 as it's very fast and effective for semantic search
25
+ return HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
26
+
27
+ def initialize_knowledge_base():
28
+ """
29
+ Checks if the vector database exists in /tmp/vectorstore.
30
+ If not, it reads the PDFs, chunks them, generates embeddings, and saves the DB.
31
+ """
32
+ global _vector_store
33
+
34
+ if os.path.exists(VECTORSTORE_DIR) and os.path.exists(os.path.join(VECTORSTORE_DIR, "index.faiss")):
35
+ print(f"Loading existing vector database from {VECTORSTORE_DIR}...")
36
+ _vector_store = FAISS.load_local(VECTORSTORE_DIR, get_embeddings_model(), allow_dangerous_deserialization=True)
37
+ return _vector_store
38
+
39
+ print("Vector database not found. Initializing knowledge base (this may take a minute)...", flush=True)
40
+ os.makedirs(VECTORSTORE_DIR, exist_ok=True)
41
+
42
+ documents = []
43
+ for pdf_file in PDF_FILES:
44
+ try:
45
+ if os.path.exists(pdf_file):
46
+ print(f"Parsing {pdf_file}...", flush=True)
47
+ loader = PyPDFLoader(pdf_file)
48
+ documents.extend(loader.load())
49
+ else:
50
+ print(f"Warning: {pdf_file} not found in the root directory.", flush=True)
51
+ except Exception as e:
52
+ print(f"Error parsing {pdf_file}: {e}", flush=True)
53
+
54
+ if not documents:
55
+ print("No documents were loaded. Vector database initialization skipped.", flush=True)
56
+ return None
57
+
58
+ print(f"Total pages loaded: {len(documents)}. Splitting text...", flush=True)
59
+
60
+ # Split the documents into manageable chunks
61
+ text_splitter = RecursiveCharacterTextSplitter(
62
+ chunk_size=1000,
63
+ chunk_overlap=200,
64
+ length_function=len
65
+ )
66
+ chunks = text_splitter.split_documents(documents)
67
+
68
+ print(f"Created {len(chunks)} text chunks. Generating embeddings...", flush=True)
69
+
70
+ # Generate embeddings and build FAISS index
71
+ embeddings = get_embeddings_model()
72
+ _vector_store = FAISS.from_documents(chunks, embeddings)
73
+
74
+ # Save for subsequent requests
75
+ _vector_store.save_local(VECTORSTORE_DIR)
76
+ print(f"Vector database built and saved to {VECTORSTORE_DIR} successfully.", flush=True)
77
+
78
+ return _vector_store
79
+
80
+ def query_rag(question: str, k: int = 3) -> str:
81
+ """
82
+ Searches the FAISS vector database for chunks related to the question.
83
+ Returns a formatted string containing the retrieved context.
84
+ """
85
+ global _vector_store
86
+
87
+ if not _vector_store:
88
+ _vector_store = initialize_knowledge_base()
89
+
90
+ if not _vector_store:
91
+ # If it's still None (e.g. PDFs missing or error), return empty context
92
+ return ""
93
+
94
+ try:
95
+ # Perform similarity search
96
+ docs = _vector_store.similarity_search(question, k=k)
97
+
98
+ # Format the retrieved documents into a single context string
99
+ context = "\n\n".join([f"[Source: {doc.metadata.get('source', 'Unknown')}]\n{doc.page_content}" for doc in docs])
100
+ return context
101
+ except Exception as e:
102
+ print(f"Error querying RAG: {e}", flush=True)
103
+ return ""
requirements.txt CHANGED
@@ -16,6 +16,27 @@ torchvision>=0.15.0
16
  Pillow>=9.0.0
17
  numpy>=1.21.0
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  # Data processing
20
  pandas>=1.3.0
21
  scikit-learn>=1.0.0
@@ -36,3 +57,12 @@ Flask-Login>=0.6.3
36
  Flask-SQLAlchemy>=3.1.1
37
  Flask-Limiter>=3.7.0
38
  psycopg2-binary>=2.9.9
 
 
 
 
 
 
 
 
 
 
16
  Pillow>=9.0.0
17
  numpy>=1.21.0
18
 
19
+ # Data processing
20
+ pandas>=1.3.0
21
+ scikit-learn>=1.0.0
22
+ # AgroGPT Requirements
23
+ # Core web framework
24
+ Flask==3.1.2
25
+ Werkzeug==3.1.3
26
+
27
+ # LLM Interface
28
+ # ollama>=0.1.0
29
+ groq>=0.9.0
30
+
31
+ # Machine Learning and AI (for Vision Model)
32
+ # Restored for Azure deployment
33
+ torch>=2.0.0
34
+ torchvision>=0.15.0
35
+
36
+ # Image processing
37
+ Pillow>=9.0.0
38
+ numpy>=1.21.0
39
+
40
  # Data processing
41
  pandas>=1.3.0
42
  scikit-learn>=1.0.0
 
57
  Flask-SQLAlchemy>=3.1.1
58
  Flask-Limiter>=3.7.0
59
  psycopg2-binary>=2.9.9
60
+
61
+ # RAG and LangChain requirements
62
+ langchain>=0.1.0
63
+ langchain-community>=0.0.10
64
+ langchain-groq>=0.0.1
65
+ langchain-huggingface>=0.0.1
66
+ pypdf>=4.0.0
67
+ faiss-cpu>=1.7.0
68
+ sentence-transformers>=2.2.0