Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| from langchain.chains import RetrievalQA | |
| from langchain.prompts import PromptTemplate | |
| from langchain_community.vectorstores import FAISS | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| from langchain_groq import ChatGroq | |
| import zipfile | |
| import os | |
| app = FastAPI() | |
| # === Startup config === | |
| class QueryRequest(BaseModel): | |
| question: str | |
| llm = None | |
| retriever = None | |
| chain = None | |
| def load_components(): | |
| global llm, retriever, chain | |
| api_key = os.getenv('api_key') | |
| # --- Load LLM --- | |
| llm = ChatGroq( | |
| model="meta-llama/llama-4-scout-17b-16e-instruct", | |
| temperature=0, | |
| max_tokens=1024, | |
| api_key=api_key | |
| ) | |
| # --- Load Embeddings --- | |
| embeddings = HuggingFaceEmbeddings( | |
| model_name="intfloat/multilingual-e5-large", | |
| model_kwargs={"device": "cpu"}, | |
| encode_kwargs={"normalize_embeddings": True}, | |
| ) | |
| # --- Unzip Vectorstore if needed --- | |
| zip_path = "faiss_index.zip" | |
| extract_path = "faiss_index" | |
| if not os.path.exists(extract_path): | |
| with zipfile.ZipFile(zip_path, 'r') as z: | |
| z.extractall(extract_path) | |
| print("โ Unzipped FAISS index.") | |
| # --- Load FAISS Vectorstore & create retriever --- | |
| vectorstore = FAISS.load_local( | |
| extract_path, | |
| embeddings, | |
| allow_dangerous_deserialization=True | |
| ) | |
| retriever = vectorstore.as_retriever(search_kwargs={"k": 5}) | |
| print("โ FAISS index loaded.") | |
| # --- Prepare prompt template --- | |
| ayat_finder_prompt = ''' | |
| You are an Arabic Ayat Finder assistant. | |
| Your goal is to provide an accurate and concise answer by extracting the exact Arabic verse (Ayah) from the provided retrieved context. | |
| Your task is to output only the exact Ayah in Arabic as it appears in the context, along with its full reference details. | |
| Instructions: | |
| 1. Locate the segment in the retrieved context that directly contains the requested Ayah. | |
| 2. Output only the Arabic text of the Ayah, without any additional commentary or translation. | |
| 3. Provide the complete reference alongside the Ayah, including: | |
| - Surah number and name (Arabic and English) | |
| - Ayah (verse) number | |
| - Any other available metadata from the context (e.g., Juz number, Hizb, revelation order) if present. | |
| 4. If the exact Ayah cannot be found in the context, respond with "ูุง ุฃุนูู ". | |
| 5. Do not add, infer, or modify any content; strictly reproduce what exists in the context. | |
| 6. if ayat is multiple time give it multiple time in output | |
| Retrieved context: | |
| {context} | |
| User's question: | |
| {question} | |
| Your response: | |
| ''' | |
| prompt = PromptTemplate( | |
| template=ayat_finder_prompt, | |
| input_variables=["context", "question"] | |
| ) | |
| # --- Assemble a stateless RetrievalQA chain (no memory) --- | |
| chain = RetrievalQA.from_chain_type( | |
| llm=llm, | |
| chain_type="stuff", | |
| retriever=retriever, | |
| return_source_documents=False, | |
| chain_type_kwargs={"prompt": prompt}, | |
| verbose=False, | |
| ) | |
| def root(): | |
| return {"message": "Quran Ayat Finder API is up."} | |
| def query(request: QueryRequest): | |
| try: | |
| result = chain.invoke({"query": request.question}) | |
| return {"answer": result["result"]} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |