quiz-poc / shared.py
Tzafrir Rehan
Remove impossible difficuly due to hallucinations
97e9a26
from openai import OpenAI
import os
from dotenv import load_dotenv
from pydantic import BaseModel
from typing import List
import random
import json
# Load environment variables from .env file
load_dotenv()
# Initialize OpenAI client with API key from environment
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:
# Use openai embeddings to calculate the cosine similarity between all questions (the question, not the choices)
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"]
# Input validation
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(
# Use the GOAT gpt-4o model
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):
# Validate number of answers
if len(q.incorrect_answers) != 3:
raise ValueError(f"Question {i} has {len(q.incorrect_answers)} incorrect answers, expected exactly 3")
# Check for duplicate answers
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": ""}]