lawallanre commited on
Commit
4908bf3
·
verified ·
1 Parent(s): 9e53cd0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -0
app.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import StreamingResponse
4
+ from pydantic import BaseModel, Field
5
+ from typing import List, Optional
6
+ from llama_index.llms.ollama import Ollama
7
+ from llama_index.core import PromptTemplate
8
+ from llama_index.llms.groq import Groq
9
+ import os
10
+ import dotenv
11
+ from enum import Enum
12
+
13
+ dotenv.load_dotenv()
14
+
15
+ app = FastAPI()
16
+
17
+ # Enable CORS for all origins (for development purposes)
18
+ app.add_middleware(
19
+ CORSMiddleware,
20
+ allow_origins=["*"],
21
+ allow_credentials=True,
22
+ allow_methods=["*"],
23
+ allow_headers=["*"],
24
+ )
25
+
26
+ # Load LLM model
27
+ # llm = Ollama(model="llama3", request_timeout=120.0)
28
+ # llama3-70b-8192
29
+ llm = Groq(model="llama-3.3-70b-versatile",
30
+ api_key=os.getenv("GROQ_API_KEY"),
31
+ )
32
+
33
+
34
+
35
+ class Options(BaseModel):
36
+ """A quiz with a title and a list of questions."""
37
+ questions: List[str] = Field(..., description="List of questions in the quiz")
38
+
39
+ class Quiz(BaseModel):
40
+ """An album inspired by a movie, containing an artist and a list of songs."""
41
+ question: str = Field(..., description="The question to be answered")
42
+ correct_answer: str = Field(..., description="The correct answer to the question")
43
+ options: List[Options] = Field(..., description="List of options for the question")
44
+
45
+ class Difficulty(str, Enum):
46
+ easy = "Easy"
47
+ medium = "Medium"
48
+ hard = "Hard"
49
+
50
+ class QuizFormat(str, Enum):
51
+ multiple_choice = "Multiple Choice"
52
+ open_ended = "Open ended"
53
+ true_false = "True/False"
54
+
55
+ class QuizRequest(BaseModel):
56
+ topic: str = Field(..., description="Topic of the quiz")
57
+ subject: str = Field(..., description="Subject of the quiz")
58
+ grade_level: str = Field(..., description="Grade level (e.g., Primary 3, JSS 1)")
59
+ description: Optional[str] = Field(None, description="Brief description of the quiz (optional)")
60
+ difficulty: Difficulty = Field(..., description="Difficulty level")
61
+ format: QuizFormat = Field(..., description="Format of the quiz")
62
+ num_questions: int = Field(..., gt=0, le=50, description="Number of questions (1-50)")
63
+ language: str = Field(..., description="Language to translate the quiz into")
64
+
65
+ # Define the prompt template for quiz generation
66
+ PROMPT_TEMPLATE = """
67
+ Generate a set of quizzes that align with the given requirements:
68
+ Topic: {topic}
69
+ Subject: {subject}
70
+ Grade Level: {grade_level}
71
+ {description_part}
72
+ Difficulty: {difficulty}
73
+ Format: {format}
74
+ Number of Questions: {num_questions}
75
+ Ensure the format follows and ensure its in json format. No extra information just your response please and make it well formatted:
76
+ [
77
+ {{
78
+ "question": "Question text here?",
79
+ "correct_answer": "Correct answer",
80
+ "options": ["Option 1", "Option 2", "Option 3", "Option 4"]
81
+ }},
82
+ ]
83
+ """
84
+
85
+ FORMAT_PROMPT_TEMPLATE = """
86
+ You are a formatting assistant. Your ONLY job is to format quizzes into a structured, teacher-friendly format.
87
+
88
+ STRICT INSTRUCTIONS:
89
+ 1. Do NOT include any explanations, introductions, or thought processes. Output ONLY the formatted quiz.
90
+ 2. Do NOT add or remove content from questions, answers, or options.
91
+ 3. Format each quiz as follows:
92
+ - Start with the question number (e.g., "1. Question text here?").
93
+ - On the next line, write "Answer: " followed by the correct answer.
94
+ - On the next line, write "Options: " followed by the list of options, each on a new line with a bullet point.
95
+ 4. Separate each quiz with two blank lines for clarity.
96
+ 5. Use STRICTLY these HTML tags for proper formatting:
97
+ - `<br>` for line breaks.
98
+ - `<p>` for paragraphs.
99
+ - `<ul>` and `<li>` for lists.
100
+
101
+ Output ONLY the formatted quiz. No extra text.
102
+
103
+ Here is the input JSON:
104
+ {quiz_json}
105
+ """
106
+
107
+ # Define the prompt template for translation
108
+ TRANSLATION_PROMPT_TEMPLATE = """
109
+ Translate the following text into {target_language}. Ensure the translation is accurate and maintains the original meaning:
110
+ {text}
111
+ """
112
+
113
+ def format_quiz_stream(prompt: str):
114
+ """Stream responses from the LLM for formatting"""
115
+ response = llm.complete(prompt)
116
+ if hasattr(response, "text"):
117
+ for chunk in response.text.split("\n"):
118
+ yield chunk
119
+ elif hasattr(response, "content"):
120
+ for chunk in response.content.split("\n"):
121
+ yield chunk
122
+ else:
123
+ raise AttributeError("Response does not contain text content.")
124
+
125
+ def translate_text(text: str, target_language: str) -> str:
126
+ """Translate text to the desired language using the LLM"""
127
+ translation_prompt = TRANSLATION_PROMPT_TEMPLATE.format(
128
+ target_language=target_language,
129
+ text=text
130
+ )
131
+ translation_response = llm.complete(translation_prompt)
132
+ return translation_response.text
133
+
134
+ @app.post("/generate-quizzes/")
135
+ def generate_quiz(quiz_request: QuizRequest):
136
+ print(quiz_request)
137
+ """Generates quizzes dynamically based on user input"""
138
+ try:
139
+ # Handle optional description
140
+ description_part = f"Description: {quiz_request.description}" if quiz_request.description else ""
141
+
142
+ # Step 1: Generate the raw quiz JSON using the first LLM
143
+ prompt = PROMPT_TEMPLATE.format(
144
+ topic=quiz_request.topic,
145
+ subject=quiz_request.subject,
146
+ grade_level=quiz_request.grade_level,
147
+ format=quiz_request.format,
148
+ description_part=description_part, # This avoids adding "Description: None" if it's not provided
149
+ difficulty=quiz_request.difficulty,
150
+ num_questions=quiz_request.num_questions
151
+ )
152
+
153
+ # Generate the raw quiz JSON
154
+ raw_quiz_response = llm.complete(prompt).text
155
+
156
+ # Step 2: Translate the raw quiz JSON into the desired language
157
+ translated_quiz_response = translate_text(raw_quiz_response, quiz_request.language)
158
+
159
+ # Step 3: Format the translated quizzes into a teacher-friendly format
160
+ format_prompt = FORMAT_PROMPT_TEMPLATE.format(
161
+ quiz_json=translated_quiz_response,
162
+
163
+ )
164
+
165
+ # Return the formatted quizzes as a streaming response
166
+ return StreamingResponse(format_quiz_stream(format_prompt), media_type="text/plain")
167
+ except Exception as e:
168
+ print(e)
169
+ raise HTTPException(status_code=500, detail=f"Internal Server Error: {str(e)}")