Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added: multiple random questions generating router #221

Merged
merged 7 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/api/src/functions/gptHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Points: [Select from the following options based on how difficult you think the
(points) => points.title,
).join(", ")}]`;

const generateChoicesPrompt = (numChoices: number) => {
export const generateChoicesPrompt = (numChoices: number) => {
let choicesPrompt = "";
for (let i = 1; i <= numChoices; i++) {
choicesPrompt += `Option ${i}: [Choice ${i}]\n`;
Expand Down
120 changes: 120 additions & 0 deletions packages/api/src/functions/randomQuestionsHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { generateChoicesPrompt, timeAndPointsPrompt } from "./gptHandlers";
import {
parseMultipleChoiceResponse,
parseMultiselectResponse,
parseIdentificationResponse,
parseTrueOrFalseResponse,
MultipleChoicePrompt,
MultiselectPrompt,
IdentificationPrompt,
TrueOrFalsePrompt,
} from "./gptHandlers";

type ParsedQuestion =
| MultipleChoicePrompt
| MultiselectPrompt
| IdentificationPrompt
| TrueOrFalsePrompt;

export type QuestionType =
| "multipleChoice"
| "multiselect"
| "identification"
| "trueOrFalse";

const questionTypes: QuestionType[] = [
"multipleChoice",
"multiselect",
"identification",
"trueOrFalse",
];
const typeCounts: Record<QuestionType, number> = {
multipleChoice: 0,
multiselect: 0,
identification: 0,
trueOrFalse: 0,
};

export const questionFormatGenerators: {
[key in QuestionType]: (
numChoices?: number,
maxCharsForQuestion?: number,
maxCharsForChoice?: number,
) => string;
} = {
multipleChoice: (
numChoices = 4,
maxCharsForQuestion = 100,
) => `separator\nQuestion: [Your question here, max ${maxCharsForQuestion} characters]
${generateChoicesPrompt(
numChoices,
)}\nCorrect Answer: Option [Correct option number] ${timeAndPointsPrompt}`,

multiselect: (
numChoices = 4,
maxCharsForQuestion = 100,
) => `separator\nQuestion: [Your question here, max ${maxCharsForQuestion} characters]
${generateChoicesPrompt(
numChoices,
)}\nAll Correct Answers: Options [Correct option numbers separated by commas, e.g., 1,3] ${timeAndPointsPrompt}`,

identification: (maxCharsForQuestion = 100, maxCharsForChoice = 68) =>
`separator\nQuestion: [Your question here, max ${maxCharsForQuestion} characters]\nAnswer: [Your answer here, max ${maxCharsForChoice} characters] ${timeAndPointsPrompt}`,

trueOrFalse: (maxCharsForQuestion = 100) =>
`separator\nQuestion: [Your question here, max ${maxCharsForQuestion} characters]\nAnswer: [True/False] ${timeAndPointsPrompt}`,
};

export const generateCombinedQuestionPrompts = (
numQuestions: number,
message: string,
): string => {
for (let i = 0; i < numQuestions; i++) {
const randomType =
questionTypes[Math.floor(Math.random() * questionTypes.length)];
typeCounts[randomType as QuestionType]++;
}

let combinedPrompts = `Based on: "${message}", create the following questions:\n`;

questionTypes.forEach((type) => {
if (typeCounts[type] > 0) {
combinedPrompts += `\n${
typeCounts[type]
} questions of type '${type}' using the format:\n${questionFormatGenerators[
type
]()}\n`;
}
});

return combinedPrompts.trim();
};

export const processGeneratedQuestions = (
generatedMessage: string,
): ParsedQuestion[] => {
const questionBlocks = generatedMessage
.split("separator")
.filter((block) => block.trim() !== "");
const parsedQuestions: ParsedQuestion[] = [];

questionBlocks.forEach((questionBlock) => {
if (questionBlock.includes("Correct Answer:")) {
parsedQuestions.push(parseMultipleChoiceResponse(questionBlock));
} else if (questionBlock.includes("All Correct Answers:")) {
parsedQuestions.push(parseMultiselectResponse(questionBlock));
} else if (
questionBlock.includes("Answer:") &&
!(questionBlock.includes("True") || questionBlock.includes("False"))
) {
parsedQuestions.push(parseIdentificationResponse(questionBlock));
} else if (
questionBlock.includes("True") ||
questionBlock.includes("False")
) {
parsedQuestions.push(parseTrueOrFalseResponse(questionBlock));
}
});

return parsedQuestions;
};
22 changes: 22 additions & 0 deletions packages/api/src/router/gptApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ import {
} from "../functions/testCreationHandlers";
import {
multipleQuestionsPromptInput,
multipleRandomQuestionsPromptInput,
singleQuestionPromptInput,
} from "../../../schema/src/gpt";
import { fetchGPT } from "../services/gptApiHandlers";
import {
generateCombinedQuestionPrompts,
processGeneratedQuestions,
} from "../functions/randomQuestionsHandlers";

export const gptApiRouter = router({
generateQuestion: protectedProcedure
Expand Down Expand Up @@ -113,4 +118,21 @@ export const gptApiRouter = router({

return answer;
}),

generateMultipleRandomQuestions: protectedProcedure
.input(multipleRandomQuestionsPromptInput)
.mutation(async ({ input }) => {
const { message, numOfQuestions } = input;

const promptText = generateCombinedQuestionPrompts(
numOfQuestions,
message,
);

const generatedMessage = await fetchGPT(promptText);

const processedQuestions = processGeneratedQuestions(generatedMessage);

return processedQuestions;
}),
});
5 changes: 5 additions & 0 deletions packages/schema/src/gpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ export const multipleQuestionsPromptInput = z.object({
maxCharsPerQuestion: z.number().optional(),
maxCharsPerChoice: z.number().optional(),
});

export const multipleRandomQuestionsPromptInput = z.object({
message: z.string(),
numOfQuestions: z.number(),
});
Loading