Spaces:
Sleeping
Sleeping
| import os | |
| import requests | |
| from flask import Flask, request, jsonify, send_from_directory | |
| from dotenv import load_dotenv | |
| # --- LangChain Imports --- | |
| from langchain_groq import ChatGroq | |
| from langchain_core.prompts import PromptTemplate | |
| from langchain_core.output_parsers import StrOutputParser | |
| # Load environment variables | |
| load_dotenv() | |
| app = Flask(__name__) | |
| # --- Configuration --- | |
| TAFSIR_OPTIONS = [ | |
| {"slug": "en-tafisr-ibn-kathir", "name": "Tafsir Ibn Kathir", "author": "Hafiz Ibn Kathir", "highlight": "A famous, highly respected classical commentary."}, | |
| {"slug": "en-tafsir-maarif-ul-quran", "name": "Maarif-ul-Quran", "author": "Mufti Muhammad Shafi", "highlight": "A renowned comprehensive modern commentary."}, | |
| {"slug": "en-al-jalalayn", "name": "Tafsir al-Jalalayn", "author": "Jalal al-Din al-Mahalli & al-Suyuti", "highlight": "A famous concise classical commentary."}, | |
| {"slug": "en-tafsir-ibn-abbas", "name": "Tanwîr al-Miqbâs", "author": "Attributed to Ibn 'Abbâs", "highlight": "One of the earliest commentaries."} | |
| ] | |
| # We will always fetch from Al-Wahidi for Shan-e-Nazool | |
| SHAN_E_NAZOOL_SLUG = "en-asbab-al-nuzul-by-al-wahidi" | |
| QURAN_API_BASE_URL = "http://api.alquran.cloud/v1/ayah" | |
| TAFSIR_API_BASE_URL = "https://cdn.jsdelivr.net/gh/spa5k/tafsir_api@main/tafsir" | |
| DATA_UNAVAILABLE_MESSAGE = "No specific commentary was found for this verse in this source." | |
| # --- LangChain Setup --- | |
| try: | |
| llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.6) | |
| # This is our new, sophisticated, all-in-one prompt | |
| prompt = PromptTemplate( | |
| template=""" | |
| You are an expert assistant for Quranic studies. Your task is to provide a clear, multi-source explanation for Surah {surah}, Ayah {ayah}. | |
| **Your response MUST be structured in the following order, using the provided data:** | |
| 1. **The Verse:** | |
| - Start with the heading: "### The Verse (Surah {surah}:{ayah})". | |
| - Quote the English translation: "{ayah_text}". | |
| 2. **Shan-e-Nazool (Reason for Revelation):** | |
| - Use the heading: "### Shan-e-Nazool (Reason for Revelation)". | |
| - Based **only** on the provided text from "Asbab Al-Nuzul by Al-Wahidi", summarize the reason for revelation. | |
| - If the text from Al-Wahidi is '{data_unavailable}', you MUST state: "No specific reason for revelation for this verse was found in Al-Wahidi's Asbab al-Nuzul." | |
| 3. **Commentary from {tafsir_name}:** | |
| - Use the heading: "### Commentary from {tafsir_name}". | |
| - Provide a comprehensive summary of the commentary from the user's chosen Tafsir ({tafsir_name} by {tafsir_author}). | |
| - If the text for this commentary is '{data_unavailable}', you MUST state: "The selected source, {tafsir_name}, does not provide a detailed commentary for this specific verse." | |
| 4. **Answer to Specific Question (If provided):** | |
| - If a user question is provided, use the heading: "### Answer to Your Question". | |
| - Answer the user's question: "{question}" | |
| - **CRITICAL:** Your answer must be based **only** on the combined information from the Al-Wahidi text and the {tafsir_name} text. | |
| - If the provided texts do not contain enough information to answer the question, state that clearly. For example: "The provided commentaries do not contain specific information to answer the question: '{question}'." | |
| - If no question is provided (i.e., the question is 'N/A'), DO NOT include this section in your output. | |
| 5. **Summary:** | |
| - Use the heading: "### Summary". | |
| - Provide a brief, final summary synthesizing the key points from the available commentaries. | |
| --- | |
| **DATA FOR YOUR TASK:** | |
| [Verse Translation]: | |
| {ayah_text} | |
| [Asbab Al-Nuzul by Al-Wahidi]: | |
| {shan_e_nazool_text} | |
| [{tafsir_name} by {tafsir_author}]: | |
| {tafsir_text} | |
| [User's Question]: | |
| {question} | |
| --- | |
| Begin your response now. | |
| """, | |
| input_variables=["surah", "ayah", "ayah_text", "shan_e_nazool_text", "tafsir_name", "tafsir_author", "tafsir_text", "question", "data_unavailable"] | |
| ) | |
| output_parser = StrOutputParser() | |
| main_chain = prompt | llm | output_parser | |
| except Exception as e: | |
| print(f"Error initializing LangChain components: {e}") | |
| main_chain = None | |
| # --- Helper Functions --- | |
| def get_data(url: str) -> str: | |
| """Generic function to fetch data and handle errors.""" | |
| try: | |
| response = requests.get(url, timeout=10) | |
| response.raise_for_status() | |
| data = response.json() | |
| text = data.get("text", "").strip() | |
| return text if text else DATA_UNAVAILABLE_MESSAGE | |
| except requests.exceptions.RequestException: | |
| return DATA_UNAVAILABLE_MESSAGE | |
| def get_ayah_text(surah: int, ayah: int) -> str: | |
| url = f"{QURAN_API_BASE_URL}/{surah}:{ayah}/en.asad" | |
| try: | |
| response = requests.get(url, timeout=10) | |
| response.raise_for_status() | |
| data = response.json() | |
| if data.get('code') == 200 and data.get('data', {}).get('text'): | |
| return data['data']['text'] | |
| return "Could not retrieve the translation for this verse." | |
| except requests.exceptions.RequestException: | |
| return "Could not retrieve the translation due to a network error." | |
| # --- Flask Routes --- | |
| def index(): | |
| return send_from_directory('.', 'index.html') | |
| def get_tafsirs(): | |
| return jsonify(TAFSIR_OPTIONS) | |
| def explain_ayah(): | |
| if not main_chain: | |
| return jsonify({"error": "LangChain services are not initialized."}), 500 | |
| data = request.get_json() | |
| surah = data.get('surah') | |
| ayah = data.get('ayah') | |
| tafsir_slug = data.get('tafsir_slug') | |
| question = data.get('question', '').strip() | |
| try: | |
| selected_tafsir = next((t for t in TAFSIR_OPTIONS if t['slug'] == tafsir_slug), None) | |
| if not selected_tafsir: | |
| return jsonify({"error": "Invalid Tafsir slug."}), 400 | |
| # --- Multi-Source Data Retrieval --- | |
| ayah_text = get_ayah_text(surah, ayah) | |
| shan_e_nazool_text = get_data(f"{TAFSIR_API_BASE_URL}/{SHAN_E_NAZOOL_SLUG}/{surah}/{ayah}.json") | |
| tafsir_text = get_data(f"{TAFSIR_API_BASE_URL}/{tafsir_slug}/{surah}/{ayah}.json") | |
| # --- Invoke the LangChain Chain --- | |
| result = main_chain.invoke({ | |
| "surah": surah, | |
| "ayah": ayah, | |
| "ayah_text": ayah_text, | |
| "shan_e_nazool_text": shan_e_nazool_text, | |
| "tafsir_name": selected_tafsir['name'], | |
| "tafsir_author": selected_tafsir['author'], | |
| "tafsir_text": tafsir_text, | |
| "question": question if question else "N/A", # Pass 'N/A' if no question | |
| "data_unavailable": DATA_UNAVAILABLE_MESSAGE | |
| }) | |
| return jsonify({"explanation": result}) | |
| except Exception as e: | |
| print(f"An error occurred in the /explain route: {e}") | |
| return jsonify({"error": "An internal server error occurred."}), 500 | |
| if __name__ == '__main__': | |
| app.run(debug=True) | |
| # #llama-3.3-70b-versatile | |