Spaces:
Runtime error
Runtime error
| """ | |
| app.py - SQL Books RAG application powered by Gradio. | |
| Retrieves relevant chunks from a FAISS index and generates answers | |
| using Llama 3 via the Groq API (free tier). | |
| """ | |
| import json | |
| import os | |
| import faiss | |
| import gradio as gr | |
| import numpy as np | |
| import requests | |
| from sentence_transformers import SentenceTransformer | |
| # -- Configuration ------------------------------------------------------------- | |
| INDEX_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "faiss_index") | |
| EMBED_MODEL = "sentence-transformers/all-MiniLM-L6-v2" | |
| GEN_MODEL = "llama-3.1-8b-instant" | |
| API_URL = "https://api.groq.com/openai/v1/chat/completions" | |
| GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "") | |
| TOP_K = 5 | |
| # -- Load resources once at startup -------------------------------------------- | |
| print("Loading embedding model ...") | |
| embedder = SentenceTransformer(EMBED_MODEL) | |
| print("Loading FAISS index ...") | |
| index = faiss.read_index(os.path.join(INDEX_DIR, "index.faiss")) | |
| print("Loading chunk metadata ...") | |
| with open(os.path.join(INDEX_DIR, "chunks.json"), "r", encoding="utf-8") as f: | |
| chunks = json.load(f) | |
| print(f"Ready: {index.ntotal} vectors, {len(chunks)} chunks") | |
| print(f"LLM: {GEN_MODEL} via Groq API") | |
| print("App ready.") | |
| # -- RAG pipeline --------------------------------------------------------------- | |
| def retrieve(query: str, top_k: int = TOP_K): | |
| """Embed the query and retrieve the top-k most similar chunks.""" | |
| query_vec = embedder.encode([query]).astype("float32") | |
| distances, indices = index.search(query_vec, top_k) | |
| results = [] | |
| for dist, idx in zip(distances[0], indices[0]): | |
| if idx < len(chunks): | |
| results.append({ | |
| "text": chunks[idx]["text"], | |
| "source": chunks[idx]["source"], | |
| "distance": float(dist), | |
| }) | |
| return results | |
| def generate_answer(query: str, context_chunks: list) -> str: | |
| """Build a prompt from retrieved context and generate an answer via Groq API.""" | |
| context = "\n\n".join( | |
| f"[Source: {c['source']}]\n{c['text'][:800]}" for c in context_chunks | |
| ) | |
| system_message = ( | |
| "You are a helpful SQL tutor. Answer the user's question using ONLY the " | |
| "provided context from SQL textbooks. Give clear, detailed explanations with " | |
| "examples where appropriate. If the context doesn't contain enough information, " | |
| "say so honestly. Format your answer using markdown." | |
| ) | |
| user_message = ( | |
| f"## Context from SQL Textbooks\n\n{context}\n\n" | |
| f"---\n\n## Question\n{query}" | |
| ) | |
| headers = { | |
| "Authorization": f"Bearer {GROQ_API_KEY}", | |
| "Content-Type": "application/json", | |
| } | |
| payload = { | |
| "model": GEN_MODEL, | |
| "messages": [ | |
| {"role": "system", "content": system_message}, | |
| {"role": "user", "content": user_message}, | |
| ], | |
| "max_tokens": 1024, | |
| "temperature": 0.3, | |
| } | |
| try: | |
| response = requests.post(API_URL, headers=headers, json=payload, timeout=60) | |
| response.raise_for_status() | |
| result = response.json() | |
| return result["choices"][0]["message"]["content"].strip() | |
| except requests.exceptions.HTTPError: | |
| return f"⚠️ API error ({response.status_code}): {response.text}" | |
| except Exception as e: | |
| return f"⚠️ Generation error: {e}" | |
| def rag_query(question: str): | |
| """Full RAG pipeline: retrieve, generate, format output.""" | |
| if not question.strip(): | |
| return "Please enter a question.", "" | |
| # Retrieve | |
| retrieved = retrieve(question) | |
| # Generate | |
| answer = generate_answer(question, retrieved) | |
| # Format sources | |
| sources_text = "\n\n---\n\n".join( | |
| f"**Source {i+1}** - *{r['source']}*\n\n{r['text'][:500]}{'...' if len(r['text']) > 500 else ''}" | |
| for i, r in enumerate(retrieved) | |
| ) | |
| return answer, sources_text | |
| # -- Gradio UI ------------------------------------------------------------------ | |
| DESCRIPTION = """ | |
| # 📚 SQL Books RAG | |
| Ask any question about SQL and get answers grounded in content from **5 SQL textbooks**: | |
| - *Practical SQL: A Beginner's Guide to Storytelling with Data* | |
| - *SQL for Data Scientists* (Renee M. Teate) | |
| - *SQL for Data Analysis: Advanced Techniques for Transforming Data into Insights* | |
| - *The Art of SQL* | |
| - *Learning SQL: Generate, Manipulate, and Retrieve Data* | |
| Powered by **FAISS** retrieval + **Llama 3.1** generation. | |
| """ | |
| EXAMPLES = [ | |
| "What is a JOIN in SQL?", | |
| "Explain the difference between INNER JOIN and LEFT JOIN", | |
| "How do window functions work in SQL?", | |
| "What is a subquery and when should I use one?", | |
| "How do I use GROUP BY with HAVING?", | |
| "What are common table expressions (CTEs)?", | |
| ] | |
| my_theme = gr.themes.Soft( | |
| primary_hue="indigo", | |
| secondary_hue="blue", | |
| ) | |
| with gr.Blocks(title="SQL Books RAG", theme=my_theme) as demo: | |
| gr.Markdown(DESCRIPTION) | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| question = gr.Textbox( | |
| label="Your SQL Question", | |
| placeholder="e.g. What is a JOIN in SQL?", | |
| lines=2, | |
| ) | |
| submit_btn = gr.Button("Ask", variant="primary", size="lg") | |
| with gr.Column(scale=1): | |
| gr.Markdown("### Try these examples") | |
| for ex in EXAMPLES: | |
| gr.Button(ex, size="sm").click( | |
| fn=lambda e=ex: e, outputs=question | |
| ) | |
| answer_box = gr.Markdown(label="Answer") | |
| with gr.Accordion("Retrieved Source Chunks", open=False): | |
| sources_box = gr.Markdown() | |
| submit_btn.click(fn=rag_query, inputs=question, outputs=[answer_box, sources_box]) | |
| question.submit(fn=rag_query, inputs=question, outputs=[answer_box, sources_box]) | |
| if __name__ == "__main__": | |
| demo.launch() | |