| from openai import OpenAI |
| import os |
| from dotenv import load_dotenv |
| from pydantic import BaseModel |
| from typing import List |
| import random |
| import json |
| |
| load_dotenv() |
|
|
| |
| client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) |
|
|
| class QuizQuestion(BaseModel): |
| question: str |
| correct_answer: str |
| incorrect_answers: List[str] |
|
|
| class QuizResponse(BaseModel): |
| questions: List[QuizQuestion] |
|
|
| def shuffle_answers(correct: str, incorrect: List[str]) -> List[str]: |
| all_answers = [correct] + incorrect |
| random.shuffle(all_answers) |
| return all_answers |
|
|
| def check_answer(choice: str, correct: str) -> str: |
| if choice == correct: |
| return "✅ Correct!" |
| return "❌ Wrong. The correct answer is: " + correct |
|
|
| def calculate_embeddings(questions: List[str]) -> float: |
| |
| embeddings = client.embeddings.create(model="text-embedding-3-small", input=questions, dimensions=512) |
| return embeddings.data |
| |
| def generate_quiz_questions(num, difficulty="Medium"): |
| genres = ["classical", "jazz", "rock", "hip-hop", "country", "pop", "electronic", "folk", "latin", "blues"] |
| time_periods = ["1900s - 1930s", "1940s", "1950s", "1960s", "1970s", "1980s", "1990s"] |
| topics = ["composers", "performers", "movements", "genres", "instruments", "events", "styles"] |
|
|
| |
| if not isinstance(num, int) or num < 1: |
| raise ValueError("Number of questions must be a positive integer") |
| |
| system_message = """You are an expert quiz generator specializing in 20th century music history, |
| with a focus on randomness and diversity of questions asked. |
| """ |
| |
| user_message = f"""Generate exactly {num} quiz questions about 20th century music history. |
| Difficulty level: {difficulty} |
| |
| For {difficulty} difficulty: |
| - Easy: Focus on well-known facts and basic knowledge |
| - Medium: Mix of common knowledge and some specific details |
| - Hard: Deep knowledge required, specific details about lesser-known facts |
| |
| Think hard to find very diverse non-trivial questions. The goal is to come up with questions that we never asked before. |
| For each question, provide the question, the correct answer, and exactly three incorrect answers. |
| |
| The questions must be about the following topics: {random.sample(topics, 3)} (do not mention the topics in the questions) |
| The questions must be about the following time periods: {random.sample(time_periods, 3)} (do not mention the time periods in the questions) |
| The questions must be about the following genres: {random.sample(genres, 3)} (do not mention the genres in the questions) |
| |
| In case of an anachronism with the genre, choose the most relevant time period. (for example, do not ask about hip hop in the 1950s) |
| """ |
|
|
| try: |
| completion = client.beta.chat.completions.parse( |
| |
| model="gpt-4o", |
| messages=[ |
| {"role": "system", "content": system_message}, |
| {"role": "user", "content": user_message} |
| ], |
| response_format=QuizResponse, |
| ) |
| |
| quiz_data = completion.choices[0].message.parsed |
|
|
| quiz_output = [] |
| for i, q in enumerate(quiz_data.questions, 1): |
| |
| if len(q.incorrect_answers) != 3: |
| raise ValueError(f"Question {i} has {len(q.incorrect_answers)} incorrect answers, expected exactly 3") |
| |
| |
| all_answers = [q.correct_answer] + q.incorrect_answers |
| if len(set(all_answers)) != 4: |
| raise ValueError(f"Question {i} has duplicate answers") |
| |
| question_text = f"### Question {i}: {q.question}" |
| shuffled = shuffle_answers(q.correct_answer, q.incorrect_answers) |
| quiz_output.append({ |
| "question": question_text, |
| "choices": shuffled, |
| "correct_answer": q.correct_answer |
| }) |
| |
| return quiz_output |
| |
| except Exception as e: |
| print(f"Error: {str(e)}") |
| return [{"question": "Error occurred", "choices": [], "correct_answer": ""}] |