areesha-06/flutter-docs-api
0
1"""2FastAPI RAG server3--------------------4Loads the FAISS index + metadata, embeds incoming queries,5retrieves top-k chunks, and calls Groq to generate an answer6grounded in those chunks.7 8Run locally:9 uvicorn app:app --reload --port 800010 11Env vars needed (.env file):12 GROQ_API_KEY=your_key_here13"""14 15import json16import os17 18import faiss19import numpy as np20from dotenv import load_dotenv21from fastapi import FastAPI22from fastapi.middleware.cors import CORSMiddleware23from groq import Groq24from pydantic import BaseModel25from sentence_transformers import SentenceTransformer26 27load_dotenv()28 29INDEX_FILE = "flutter_docs.index"30METADATA_FILE = "flutter_docs_meta.jsonl"31MODEL_NAME = "all-MiniLM-L6-v2"32TOP_K = 433 34app = FastAPI(title="Flutter Docs RAG API")35 36# Allow the React frontend (adjust origin for production deploy)37app.add_middleware(38 CORSMiddleware,39 allow_origins=["*"],40 allow_methods=["*"],41 allow_headers=["*"],42)43 44print("Loading embedding model...")45embed_model = SentenceTransformer(MODEL_NAME)46 47print("Loading FAISS index...")48index = faiss.read_index(INDEX_FILE)49 50print("Loading metadata...")51metadata = []52with open(METADATA_FILE, "r", encoding="utf-8") as f:53 for line in f:54 metadata.append(json.loads(line))55 56groq_client = Groq(api_key=os.getenv("GROQ_API_KEY"))57 58 59class QueryRequest(BaseModel):60 question: str61 62 63def retrieve(question: str, k: int = TOP_K):64 query_vec = embed_model.encode([question], convert_to_numpy=True).astype("float32")65 faiss.normalize_L2(query_vec)66 scores, indices = index.search(query_vec, k)67 results = []68 for score, idx in zip(scores[0], indices[0]):69 if idx == -1:70 continue71 chunk = metadata[idx]72 results.append({**chunk, "score": float(score)})73 return results74 75 76def build_prompt(question: str, chunks: list):77 context = "\n\n---\n\n".join(78 f"Source: {c['title']} ({c['url']})\n{c['text']}" for c in chunks79 )80 return f"""You are a helpful assistant answering questions about Flutter using only the provided documentation excerpts. If the answer isn't in the excerpts, say you don't know rather than guessing.81 82Documentation excerpts:83{context}84 85Question: {question}86 87Answer clearly and concisely. Cite the source URL(s) you used at the end."""88 89 90@app.post("/query")91def query(req: QueryRequest):92 chunks = retrieve(req.question)93 prompt = build_prompt(req.question, chunks)94 95 completion = groq_client.chat.completions.create(96 # Groq's model lineup changes fairly often -- if this errors with97 # "model not found", check console.groq.com/docs/models for the98 # current list and swap the id here.99 model="llama-3.3-70b-versatile",100 messages=[{"role": "user", "content": prompt}],101 temperature=0.2,102 )103 104 answer = completion.choices[0].message.content105 sources = list({c["url"] for c in chunks})106 107 return {"answer": answer, "sources": sources}108 109 110@app.get("/health")111def health():112 return {"status": "ok", "chunks_loaded": len(metadata)}113 