Initial commit
This commit is contained in:
commit
c87ebf0a74
22 changed files with 4973 additions and 0 deletions
86
services/geminiService.ts
Normal file
86
services/geminiService.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { GoogleGenAI, Type } from "@google/genai";
|
||||
import { Quiz, Question, AnswerOption } from "../types";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const getClient = () => {
|
||||
const apiKey = process.env.API_KEY;
|
||||
if (!apiKey) {
|
||||
throw new Error("API_KEY environment variable is missing");
|
||||
}
|
||||
return new GoogleGenAI({ apiKey });
|
||||
};
|
||||
|
||||
export const generateQuiz = async (topic: string): Promise<Quiz> => {
|
||||
const ai = getClient();
|
||||
|
||||
const prompt = `Generate a trivia quiz about "${topic}". Create 10 engaging multiple-choice questions. Each question must have exactly 4 options, and exactly one correct answer. Vary the difficulty.`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: "gemini-3-flash-preview",
|
||||
contents: prompt,
|
||||
config: {
|
||||
responseMimeType: "application/json",
|
||||
responseSchema: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
title: { type: Type.STRING, description: "A catchy title for the quiz" },
|
||||
questions: {
|
||||
type: Type.ARRAY,
|
||||
items: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
text: { type: Type.STRING, description: "The question text" },
|
||||
options: {
|
||||
type: Type.ARRAY,
|
||||
items: {
|
||||
type: Type.OBJECT,
|
||||
properties: {
|
||||
text: { type: Type.STRING },
|
||||
isCorrect: { type: Type.BOOLEAN }
|
||||
},
|
||||
required: ["text", "isCorrect"]
|
||||
},
|
||||
}
|
||||
},
|
||||
required: ["text", "options"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["title", "questions"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.text) {
|
||||
throw new Error("Failed to generate quiz content");
|
||||
}
|
||||
|
||||
const data = JSON.parse(response.text);
|
||||
|
||||
// Transform to our internal type with shapes/colors pre-assigned
|
||||
const questions: Question[] = data.questions.map((q: any) => {
|
||||
const shapes = ['triangle', 'diamond', 'circle', 'square'] as const;
|
||||
const colors = ['red', 'blue', 'yellow', 'green'] as const;
|
||||
|
||||
// Shuffle options so the correct one isn't always first (though Gemini usually randomizes, safety first)
|
||||
// Actually, to map shapes consistently, let's keep array order but assign props
|
||||
const options: AnswerOption[] = q.options.map((opt: any, index: number) => ({
|
||||
text: opt.text,
|
||||
isCorrect: opt.isCorrect,
|
||||
shape: shapes[index % 4],
|
||||
color: colors[index % 4]
|
||||
}));
|
||||
|
||||
return {
|
||||
id: uuidv4(),
|
||||
text: q.text,
|
||||
options: options,
|
||||
timeLimit: 20
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
title: data.title,
|
||||
questions
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue