import os import pandas as pd import requests from sentence_transformers import SentenceTransformer from pinecone import Pinecone from openai import OpenAI import re # ================= ENV ================= PINECONE_API_KEY = os.getenv("PINECONE_API_KEY") OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # ================= CONSTANTS ================= SERVICES_INDEX_NAME = "aaple-sarkar-services" BASICS_INDEX_NAME = "aaplesarkarbasics" BASE_DIR = os.path.dirname(__file__) DATA_DIR = os.path.join(BASE_DIR, "data") # ================= INIT ================= pc = Pinecone(api_key=PINECONE_API_KEY) if PINECONE_API_KEY else None client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None embedder = SentenceTransformer("all-MiniLM-L6-v2") services_index = pc.Index(SERVICES_INDEX_NAME) if pc else None basics_index = pc.Index(BASICS_INDEX_NAME) if pc else None # ================= LOAD DATA ================= EXCEL_URL = "https://huggingface.co/datasets/PrathameshRaut/aaple-sarkar-data/resolve/main/Untitled%20spreadsheet-2.xlsx" LOCAL_EXCEL_PATH = os.path.join(DATA_DIR, "services.xlsx") os.makedirs(DATA_DIR, exist_ok=True) if not os.path.exists(LOCAL_EXCEL_PATH): r = requests.get(EXCEL_URL) r.raise_for_status() with open(LOCAL_EXCEL_PATH, "wb") as f: f.write(r.content) excel_df = pd.read_excel( LOCAL_EXCEL_PATH, sheet_name="Copy of Notified services (1212" ) # ================= SEARCH ================= def is_small_talk(text: str) -> bool: text = text.strip().lower() return bool(re.fullmatch( r"(hi|hello|hey|hii|hai|namaste|thanks|thank you|ok|okay|yes|no|namaskar|good morning|good afternoon|good evening|good night|how are you?|how are you|how are you.|who are you?|introduce yourself|introduce)", text )) def search_services(query, min_score=0.75): if not services_index: return None emb = embedder.encode([query])[0].tolist() res = services_index.query( vector=emb, top_k=1, include_metadata=True ) if not res.matches: return None match = res.matches[0] if match.score < min_score: return None return match.metadata["text"] def search_basics(query): if not basics_index: return "" emb = embedder.encode([query])[0].tolist() res = basics_index.query( vector=emb, top_k=5, include_metadata=True ) return "\n\n".join(m.metadata["text"] for m in res.matches) def get_service_info(dept, service): mask = ( (excel_df["Department"].str.lower() == dept.lower()) & (excel_df["Service Name"] .str.lower() .str.contains(service.lower(), na=False)) ) row = excel_df[mask] if row.empty: return None return { k: v for k, v in row.iloc[0].to_dict().items() if pd.notna(v) and str(v).strip() } # ================= RESPONSE ================= def generate_response(user_query, language="en"): # ================= GUARD ================= if not client or not pc: return "

❌ Server misconfiguration: API keys are missing. Please contact the administrator.

" # ================= SMALL TALK SHORT-CIRCUIT ================= if is_small_talk(user_query): prompt = f""" You are Aaple Sarkar Services Chatbot (Maharashtra Government) named "Aapla Sahayak". Rules: - Respond politely, briefly, and naturally using some 1-2 emojis, also while greeting say Namaskar. - Respond with RAW, valid, render-ready HTML only. - Do not use markdown. - Do not add explanations outside HTML. User Query: {user_query} """ response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "system", "content": prompt}], temperature=0.3, max_tokens=150, ) return response.choices[0].message.content # ================= RAG PIPELINE ================= basics_context = search_basics(user_query) service_match = search_services(user_query) context_parts = [] if basics_context: context_parts.append( f"General Aaple Sarkar Information:\n{basics_context}" ) if service_match: try: dept_part, service_part = service_match.split(" - Service: ") dept = dept_part.replace("Department:", "").strip() service = service_part.strip() info = get_service_info(dept, service) context_parts.append( f"Detected Service:\nDepartment: {dept}\nService: {service}" ) if info: context_parts.append( "Service Details:\n" + "\n".join( f"{k}: {v}" for k, v in list(info.items())[:20] ) ) except Exception: pass context = "\n\n".join(context_parts) or "No relevant data found." # ================= MAIN PROMPT ================= prompt = f""" You are Aaple Sarkar Services Chatbot (Maharashtra Government). Rules: - Respond with RAW, valid, render-ready HTML only; do not use markdown, do not wrap the response in ``` or ```html, do not add explanations/comments/text outside HTML, do not add leading or trailing whitespace, and ensure the response starts directly with or the first HTML tag. - If the user message is a generic, short, or social message (such as greetings, acknowledgements, or fillers), you must respond appropriately with a polite, natural, and context-independent reply. User Query: {user_query} Available Knowledge: {context} Provide complete guidance including: - Eligibility - Documents required - Fees - Timeline - Officers involved - Online application steps """ response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "system", "content": prompt}], temperature=0.6, max_tokens=800, ) return response.choices[0].message.content