Update repo
This commit is contained in:
116
packages/backend/convex/questions.ts
Normal file
116
packages/backend/convex/questions.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { v } from "convex/values";
|
||||
import { query, mutation, action } from "./_generated/server";
|
||||
import { getAuthUserId } from "@convex-dev/auth/server";
|
||||
import { api } from "./_generated/api";
|
||||
import OpenAI from "openai";
|
||||
|
||||
const openai = new OpenAI({
|
||||
baseURL: process.env.OPENAI_BASE_URL,
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
export const getAllQuestions = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (!userId) throw new Error("Not authenticated");
|
||||
return await ctx.db.query("questions").collect();
|
||||
},
|
||||
});
|
||||
|
||||
export const getQuestionsByTopic = query({
|
||||
args: { topic: v.string() },
|
||||
handler: async (ctx, args) => {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (!userId) throw new Error("Not authenticated");
|
||||
return await ctx.db
|
||||
.query("questions")
|
||||
.withIndex("by_topic", (q) => q.eq("topic", args.topic))
|
||||
.collect();
|
||||
},
|
||||
});
|
||||
|
||||
export const addQuestion = mutation({
|
||||
args: {
|
||||
question: v.string(),
|
||||
options: v.array(v.string()),
|
||||
correctAnswer: v.number(),
|
||||
topic: v.string(),
|
||||
difficulty: v.optional(v.string()),
|
||||
explanation: v.optional(v.string()),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (!userId) throw new Error("Not authenticated");
|
||||
return await ctx.db.insert("questions", {
|
||||
question: args.question,
|
||||
options: args.options,
|
||||
correctAnswer: args.correctAnswer,
|
||||
topic: args.topic,
|
||||
difficulty: args.difficulty,
|
||||
explanation: args.explanation,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const generateQuestions = action({
|
||||
args: {
|
||||
topic: v.string(),
|
||||
count: v.number(),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (!userId) throw new Error("Not authenticated");
|
||||
const prompt = `Generate ${args.count} multiple choice questions for the CompTIA Network+ exam on the topic: "${args.topic}".
|
||||
|
||||
Return ONLY a valid JSON array with this exact structure:
|
||||
[
|
||||
{
|
||||
"question": "Question text here?",
|
||||
"options": ["Option A", "Option B", "Option C", "Option D"],
|
||||
"correctAnswer": 0,
|
||||
"topic": "${args.topic}",
|
||||
"difficulty": "medium",
|
||||
"explanation": "Brief explanation of the correct answer"
|
||||
}
|
||||
]
|
||||
|
||||
Rules:
|
||||
- Each question must have exactly 4 options
|
||||
- correctAnswer is the index (0-3) of the correct option
|
||||
- Make questions realistic for Network+ certification
|
||||
- Include technical details and scenarios
|
||||
- Return ONLY the JSON array, no other text`;
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: "gpt-4o-mini",
|
||||
messages: [{ role: "user", content: prompt }],
|
||||
temperature: 0.8,
|
||||
});
|
||||
|
||||
const content = response.choices[0].message.content;
|
||||
if (!content) throw new Error("No response from AI");
|
||||
|
||||
let questions;
|
||||
try {
|
||||
questions = JSON.parse(content);
|
||||
} catch (e) {
|
||||
throw new Error("Failed to parse AI response as JSON");
|
||||
}
|
||||
|
||||
const questionIds = [];
|
||||
for (const q of questions) {
|
||||
const id: string = await ctx.runMutation(api.questions.addQuestion, {
|
||||
question: q.question,
|
||||
options: q.options,
|
||||
correctAnswer: q.correctAnswer,
|
||||
topic: q.topic,
|
||||
difficulty: q.difficulty,
|
||||
explanation: q.explanation,
|
||||
});
|
||||
questionIds.push(id);
|
||||
}
|
||||
|
||||
return questionIds;
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user