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 | |
import os | |
os.environ["TOKENIZERS_PARALLELISM"] = "false" | |
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": 3}) | |
print("โ FAISS index loaded.") | |
# --- Prepare prompt template --- | |
quiz_solving_prompt = """ | |
You are an Arabic Hadith Finder assistant. | |
Your goal is to provide an accurate and concise answer extracted directly from the provided retrieved context. | |
Your task is to output only the exact Arabic Hadith (as it appears in the context), removing any extraneous or irrelevant data. | |
Instructions: | |
1. Identify the segment in the retrieved context that directly answers the user's question. | |
2. Output the Hadith exactly as it appears in Arabic in the context. | |
3. Remove any information that does not pertain directly to the query. | |
4. If the context does not contain sufficient information to answer the question, respond with "ูุง ุฃุนูู ". Do not add or infer any extra information. | |
5. Provide the complete reference of the Hadith (if available), including: | |
- Chapter Number and Name | |
- Section Number and Name | |
- Hadith Number | |
- Arabic Isnad and Matn | |
- Arabic Grade (if present) | |
- Hadith Book name | |
Retrieved context: | |
{context} | |
User's question: | |
{question} | |
Your response: | |
""" | |
prompt = PromptTemplate( | |
template=quiz_solving_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": "Arabic Hadith 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)) | |