File size: 3,965 Bytes
2748d2d
 
 
f64547d
 
 
2748d2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87668f1
5ca56e1
f64547d
87668f1
 
 
 
f64547d
2748d2d
 
87668f1
f64547d
87668f1
2748d2d
 
 
 
 
87668f1
2748d2d
 
 
 
 
 
 
 
 
 
 
 
87668f1
2748d2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87668f1
2748d2d
87668f1
2748d2d
 
87668f1
 
2748d2d
 
 
 
 
 
87668f1
2748d2d
87668f1
2748d2d
87668f1
2748d2d
 
 
 
 
 
 
 
 
 
87668f1
f64547d
2748d2d
 
f64547d
5edff09
2748d2d
87668f1
2748d2d
 
 
 
87668f1
 
2748d2d
 
 
 
 
87668f1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import os
import zipfile
import logging
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_groq import ChatGroq
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)

app = FastAPI()

# === Globals ===
llm = None
embeddings = None
vectorstore = None
retriever = None
quiz_chain = None
grade_chain = None

class QuizRequest(BaseModel):
    question: str

class GradeRequest(BaseModel):
    question: str  # string of Q/A pairs

@app.on_event("startup")
def load_components():
    global llm, embeddings, vectorstore, retriever, quiz_chain, grade_chain
    try:
        api_key = os.getenv("api_key")
        if not api_key:
            logger.error("API_KEY environment variable is not set or empty.")
            raise RuntimeError("API_KEY environment variable is not set or empty.")
        logger.info("API_KEY is set.")

        # 1) Init LLM & Embeddings
        llm = ChatGroq(
            model="meta-llama/llama-4-scout-17b-16e-instruct",
            temperature=0,
            max_tokens=1024,
            api_key=api_key,
        )
        embeddings = HuggingFaceEmbeddings(
            model_name="intfloat/multilingual-e5-large",
            model_kwargs={"device": "cpu"},
            encode_kwargs={"normalize_embeddings": True},
        )

        # 2) Load FAISS indexes
        for zip_name, dir_name in [("faiss_index.zip", "faiss_index"), ("faiss_index(1).zip", "faiss_index_extra")]:
            if not os.path.exists(dir_name):
                with zipfile.ZipFile(zip_name, 'r') as z:
                    z.extractall(dir_name)
                logger.info(f"Unzipped {zip_name} to {dir_name}.")
            else:
                logger.info(f"Directory {dir_name} already exists.")

        vs1 = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True)
        logger.info("FAISS index 1 loaded.")
        vs2 = FAISS.load_local("faiss_index_extra", embeddings, allow_dangerous_deserialization=True)
        logger.info("FAISS index 2 loaded.")

        vs1.merge_from(vs2)
        vectorstore = vs1
        logger.info("Merged FAISS indexes into a single vectorstore.")

        retriever = vectorstore.as_retriever(search_kwargs={"k": 10})

        # Quiz generation chain
        quiz_prompt = PromptTemplate(
            template="""
Generate a quiz on the topic "{question}" using **only** the information in the "Retrieved context".
Include clear questions and multiple-choice options (A, B, C, D). Also provide the answers of the questions with them.
If context is insufficient, reply with "I don't know".

Retrieved context:
{context}

Quiz topic:
{question}

Quiz:
""",
            input_variables=["context", "question"],
        )
        quiz_chain = RetrievalQA.from_chain_type(
            llm=llm,
            chain_type="stuff",
            retriever=retriever,
            return_source_documents=False,
            chain_type_kwargs={"prompt": quiz_prompt},
        )
        logger.info("Quiz chain ready.")

       
    except Exception as e:
        logger.error("Error loading components", exc_info=True)
        raise

@app.get("/")
def root():
    return {"message": "API is up and running!"}

@app.post("/quiz")
def create_quiz(request: QuizRequest):
    try:
        logger.info("Generating quiz for topic: %s", request.question)
        result = quiz_chain.invoke({"query": request.question})
        logger.info("Quiz generated successfully.")
        return {"quiz": result.get("result")}
    except Exception as e:
        logger.error("Error generating quiz", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))