File size: 6,396 Bytes
4908bf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
03d1c7c
4908bf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0f8eb8
 
 
 
 
4908bf3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from typing import List, Optional
from llama_index.llms.ollama import Ollama
from llama_index.core import PromptTemplate
from llama_index.llms.groq import Groq
import os
import dotenv
from enum import Enum

dotenv.load_dotenv()

app = FastAPI()

# Enable CORS for all origins (for development purposes)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Load LLM model
# llm = Ollama(model="llama3", request_timeout=120.0)
# llama3-70b-8192
llm = Groq(model="llama-3.3-70b-versatile", 
           api_key="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",
    )



class Options(BaseModel):
    """A quiz with a title and a list of questions."""
    questions: List[str] = Field(..., description="List of questions in the quiz")

class Quiz(BaseModel):
    """An album inspired by a movie, containing an artist and a list of songs."""
    question: str = Field(..., description="The question to be answered")
    correct_answer: str = Field(..., description="The correct answer to the question")
    options: List[Options] = Field(..., description="List of options for the question")

class Difficulty(str, Enum):
    easy = "Easy"
    medium = "Medium"
    hard = "Hard"

class QuizFormat(str, Enum):
    multiple_choice = "Multiple Choice"
    open_ended = "Open ended"
    true_false = "True/False"

class QuizRequest(BaseModel):
    topic: str = Field(..., description="Topic of the quiz")
    subject: str = Field(..., description="Subject of the quiz")
    grade_level: str = Field(..., description="Grade level (e.g., Primary 3, JSS 1)")
    description: Optional[str] = Field(None, description="Brief description of the quiz (optional)")
    difficulty: Difficulty = Field(..., description="Difficulty level")
    format: QuizFormat = Field(..., description="Format of the quiz")
    num_questions: int = Field(..., gt=0, le=50, description="Number of questions (1-50)")
    language: str = Field(..., description="Language to translate the quiz into")

# Define the prompt template for quiz generation
PROMPT_TEMPLATE = """
Generate a set of quizzes that align with the given requirements:
Topic: {topic}
Subject: {subject}
Grade Level: {grade_level}
{description_part}
Difficulty: {difficulty}
Format: {format}
Number of Questions: {num_questions}
Ensure the format follows and ensure its in json format. No extra information just your response please and make it well formatted: 
[
    {{
        "question": "Question text here?",
        "correct_answer": "Correct answer",
        "options": ["Option 1", "Option 2", "Option 3", "Option 4"]
    }},
]
"""

FORMAT_PROMPT_TEMPLATE = """
You are a formatting assistant. Your ONLY job is to format quizzes into a structured, teacher-friendly format. 

STRICT INSTRUCTIONS:
1. Do NOT include any explanations, introductions, or thought processes. Output ONLY the formatted quiz.
2. Do NOT add or remove content from questions, answers, or options.
3. Format each quiz as follows:
   - Start with the question number (e.g., "1. Question text here?").
   - On the next line, write "Answer: " followed by the correct answer.
   - On the next line, write "Options: " followed by the list of options, each on a new line with a bullet point.
4. Separate each quiz with two blank lines for clarity.
5. Use STRICTLY these HTML tags for proper formatting:
   - `<br>` for line breaks.
   - `<p>` for paragraphs.
   - `<ul>` and `<li>` for lists.

Output ONLY the formatted quiz. No extra text.

Here is the input JSON:
{quiz_json}
"""

# Define the prompt template for translation
TRANSLATION_PROMPT_TEMPLATE = """
Translate the following text into {target_language}. Ensure the translation is accurate and maintains the original meaning:
{text}
"""

def format_quiz_stream(prompt: str):
    """Stream responses from the LLM for formatting"""
    response = llm.complete(prompt)
    if hasattr(response, "text"):
        for chunk in response.text.split("\n"):
            yield chunk
    elif hasattr(response, "content"):
        for chunk in response.content.split("\n"):
            yield chunk
    else:
        raise AttributeError("Response does not contain text content.")

def translate_text(text: str, target_language: str) -> str:
    """Translate text to the desired language using the LLM"""
    translation_prompt = TRANSLATION_PROMPT_TEMPLATE.format(
        target_language=target_language,
        text=text
    )
    translation_response = llm.complete(translation_prompt)
    return translation_response.text


@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.post("/generate-quizzes/")
def generate_quiz(quiz_request: QuizRequest):
    print(quiz_request)
    """Generates quizzes dynamically based on user input"""
    try:
        # Handle optional description
        description_part = f"Description: {quiz_request.description}" if quiz_request.description else ""

        # Step 1: Generate the raw quiz JSON using the first LLM
        prompt = PROMPT_TEMPLATE.format(
            topic=quiz_request.topic,
            subject=quiz_request.subject,
            grade_level=quiz_request.grade_level,
            format=quiz_request.format,
            description_part=description_part,  # This avoids adding "Description: None" if it's not provided
            difficulty=quiz_request.difficulty,
            num_questions=quiz_request.num_questions
        )

        # Generate the raw quiz JSON
        raw_quiz_response = llm.complete(prompt).text

        # Step 2: Translate the raw quiz JSON into the desired language
        translated_quiz_response = translate_text(raw_quiz_response, quiz_request.language)

        # Step 3: Format the translated quizzes into a teacher-friendly format
        format_prompt = FORMAT_PROMPT_TEMPLATE.format(
            quiz_json=translated_quiz_response,
            
            )

        # Return the formatted quizzes as a streaming response
        return StreamingResponse(format_quiz_stream(format_prompt), media_type="text/plain")
    except Exception as e:
        print(e)
        raise HTTPException(status_code=500, detail=f"Internal Server Error: {str(e)}")